Apply timeout to Fsecure malware response. Bug 1549
[exim.git] / src / src / malware.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) Tom Kistner <tom@duncanthrax.net> 2003-2014 */
6 /* License: GPL */
7
8 /* Code for calling virus (malware) scanners. Called from acl.c. */
9
10 #include "exim.h"
11 #ifdef WITH_CONTENT_SCAN
12
13 typedef enum {M_FPROTD, M_DRWEB, M_AVES, M_FSEC, M_KAVD, M_CMDL,
14                 M_SOPHIE, M_CLAMD, M_SOCK, M_MKSD, M_AVAST} scanner_t;
15 typedef enum {MC_NONE, MC_TCP, MC_UNIX, MC_STRM} contype_t;
16 static struct scan
17 {
18   scanner_t     scancode;
19   const uschar * name;
20   const uschar * options_default;
21   contype_t     conn;
22 } m_scans[] =
23 {
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   { M_AVAST,    US"avast",      US"/var/run/avast/scan.sock",         MC_STRM },
35   { -1,         NULL,           NULL, MC_NONE }         /* end-marker */
36 };
37
38 /* The maximum number of clamd servers that are supported in the configuration */
39 #define MAX_CLAMD_SERVERS 32
40 #define MAX_CLAMD_SERVERS_S "32"
41 /* Maximum length of the hostname that can be specified in the clamd address list */
42 #define MAX_CLAMD_ADDRESS_LENGTH 64
43 #define MAX_CLAMD_ADDRESS_LENGTH_S "64"
44
45 typedef struct clamd_address_container {
46   uschar tcp_addr[MAX_CLAMD_ADDRESS_LENGTH+1];
47   unsigned int tcp_port;
48 } clamd_address_container;
49
50 /* declaration of private routines */
51 static int mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename);
52 static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
53
54 #ifndef nelements
55 # define nelements(arr) (sizeof(arr) / sizeof(arr[0]))
56 #endif
57
58
59 #define        MALWARE_TIMEOUT             120
60
61
62 #define DRWEBD_SCAN_CMD             (1)     /* scan file, buffer or diskfile */
63 #define DRWEBD_RETURN_VIRUSES       (1<<0)   /* ask daemon return to us viruses names from report */
64 #define DRWEBD_IS_MAIL              (1<<19)  /* say to daemon that format is "archive MAIL" */
65
66 #define DERR_READ_ERR               (1<<0)   /* read error */
67 #define DERR_NOMEMORY               (1<<2)   /* no memory */
68 #define DERR_TIMEOUT                (1<<9)   /* scan timeout has run out */
69 #define DERR_BAD_CALL               (1<<15)  /* wrong command */
70
71 /* Routine to check whether a system is big- or little-endian.
72    Ripped from http://www.faqs.org/faqs/graphics/fileformats-faq/part4/section-7.html
73    Needed for proper kavdaemon implementation. Sigh. */
74 #define BIG_MY_ENDIAN      0
75 #define LITTLE_MY_ENDIAN   1
76 static int test_byte_order(void);
77 static inline int
78 test_byte_order()
79 {
80   short int word = 0x0001;
81   char *byte = (char *) &word;
82   return(byte[0] ? LITTLE_MY_ENDIAN : BIG_MY_ENDIAN);
83 }
84
85 BOOL malware_ok = FALSE;
86
87 /* Gross hacks for the -bmalware option; perhaps we should just create
88 the scan directory normally for that case, but look into rigging up the
89 needed header variables if not already set on the command-line? */
90 extern int spool_mbox_ok;
91 extern uschar spooled_message_id[17];
92
93 /*************************************************
94 *          Scan an email for malware             *
95 *************************************************/
96
97 /* This is the normal interface for scanning an email, which doesn't need a
98 filename; it's a wrapper around the malware_file function.
99
100 Arguments:
101   listptr     the list of options to the "malware = ..." ACL condition
102
103 Returns:      Exim message processing code (OK, FAIL, DEFER, ...)
104               where true means malware was found (condition applies)
105 */
106 int
107 malware(uschar **listptr)
108 {
109   uschar * scan_filename;
110   int ret;
111
112   scan_filename = string_sprintf("%s/scan/%s/%s.eml",
113                     spool_directory, message_id, message_id);
114   ret = malware_internal(listptr, scan_filename, FALSE);
115   if (ret == DEFER) av_failed = TRUE;
116
117   return ret;
118 }
119
120
121 /*************************************************
122 *          Scan a file for malware               *
123 *************************************************/
124
125 /* This is a test wrapper for scanning an email, which is not used in
126 normal processing.  Scan any file, using the Exim scanning interface.
127 This function tampers with various global variables so is unsafe to use
128 in any other context.
129
130 Arguments:
131   eml_filename  a file holding the message to be scanned
132
133 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
134                 where true means malware was found (condition applies)
135 */
136 int
137 malware_in_file(uschar *eml_filename)
138 {
139   uschar *scan_options[2];
140   uschar message_id_buf[64];
141   int ret;
142
143   scan_options[0] = US"*";
144   scan_options[1] = NULL;
145
146   /* spool_mbox() assumes various parameters exist, when creating
147   the relevant directory and the email within */
148   (void) string_format(message_id_buf, sizeof(message_id_buf),
149       "dummy-%d", vaguely_random_number(INT_MAX));
150   message_id = message_id_buf;
151   sender_address = US"malware-sender@example.net";
152   return_path = US"";
153   recipients_list = NULL;
154   receive_add_recipient(US"malware-victim@example.net", -1);
155   enable_dollar_recipients = TRUE;
156
157   ret = malware_internal(scan_options, eml_filename, TRUE);
158
159   Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
160   spool_mbox_ok = 1;
161   /* don't set no_mbox_unspool; at present, there's no way for it to become
162   set, but if that changes, then it should apply to these tests too */
163   unspool_mbox();
164
165   /* silence static analysis tools */
166   message_id = NULL;
167
168   return ret;
169 }
170
171
172 static inline int
173 malware_errlog_defer(const uschar * str)
174 {
175   log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str);
176   return DEFER;
177 }
178
179 static int
180 m_errlog_defer(struct scan * scanent, const uschar * str)
181 {
182   return malware_errlog_defer(string_sprintf("%s: %s", scanent->name, str));
183 }
184 static int
185 m_errlog_defer_3(struct scan * scanent, const uschar * str,
186         int fd_to_close)
187 {
188   (void) close(fd_to_close);
189   return m_errlog_defer(scanent, str);
190 }
191
192 /*************************************************/
193
194 /* Only used by the Clamav code, which is working from a list of servers and
195 uses the returned in_addr to get a second connection to the same system.
196 */
197 static inline int
198 m_tcpsocket(const uschar * hostname, unsigned int port,
199         host_item * host, uschar ** errstr)
200 {
201   return ip_connectedsocket(SOCK_STREAM, hostname, port, port, 5, host, errstr);
202 }
203
204 static int
205 m_tcpsocket_fromdef(const uschar * hostport, uschar ** errstr)
206 {
207   int scan;
208   uschar hostname[256];
209   unsigned int portlow, porthigh;
210
211   /* extract host and port part */
212   scan = sscanf(CS hostport, "%255s %u-%u", hostname, &portlow, &porthigh);
213   if ( scan != 3 ) {
214     if ( scan != 2 ) {
215       *errstr = string_sprintf("invalid socket '%s'", hostport);
216       return -1;
217     }
218     porthigh = portlow;
219   }
220
221   return ip_connectedsocket(SOCK_STREAM, hostname, portlow, porthigh,
222                             5, NULL, errstr);
223 }
224
225 static int
226 m_unixsocket(const uschar * path, uschar ** errstr)
227 {
228   int sock;
229   struct sockaddr_un server;
230
231   if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
232     *errstr = US"can't open UNIX socket.";
233     return -1;
234   }
235
236   server.sun_family = AF_UNIX;
237   Ustrncpy(server.sun_path, path, sizeof(server.sun_path)-1);
238   server.sun_path[sizeof(server.sun_path)-1] = '\0';
239   if (connect(sock, (struct sockaddr *) &server, sizeof(server)) < 0) {
240     int err = errno;
241     (void)close(sock);
242     *errstr =  string_sprintf("unable to connect to UNIX socket (%s): %s",
243                   path, strerror(err));
244     return -1;
245     }
246   return sock;
247 }
248
249 static inline int
250 m_streamsocket(const uschar * spec, uschar ** errstr)
251 {
252   return *spec == '/'
253     ? m_unixsocket(spec, errstr) : m_tcpsocket_fromdef(spec, errstr);
254 }
255
256 static int
257 m_sock_send(int sock, uschar * buf, int cnt, uschar ** errstr)
258 {
259   if (send(sock, buf, cnt, 0) < 0) {
260     int err = errno;
261     (void)close(sock);
262     *errstr = string_sprintf("unable to send to socket (%s): %s",
263            buf, strerror(err));
264     return -1;
265     }
266   return sock;
267 }
268
269 static const pcre *
270 m_pcre_compile(const uschar * re, uschar ** errstr)
271 {
272   const uschar * rerror;
273   int roffset;
274   const pcre * cre;
275
276   cre = pcre_compile(CS re, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
277   if (!cre)
278     *errstr= string_sprintf("regular expression error in '%s': %s at offset %d",
279         re, rerror, roffset);
280   return cre;
281 }
282
283 uschar *
284 m_pcre_exec(const pcre * cre, uschar * text)
285 {
286   int ovector[10*3];
287   int i = pcre_exec(cre, NULL, CS text, Ustrlen(text), 0, 0,
288                 ovector, nelements(ovector));
289   uschar * substr = NULL;
290   if (i >= 2)                           /* Got it */
291     pcre_get_substring(CS text, ovector, i, 1, (const char **) &substr);
292   return substr;
293 }
294
295 static const pcre *
296 m_pcre_nextinlist(uschar ** list, int * sep, char * listerr, uschar ** errstr)
297 {
298   const uschar * list_ele;
299   const pcre * cre = NULL;
300
301   if (!(list_ele = string_nextinlist(list, sep, NULL, 0)))
302     *errstr = US listerr;
303   else
304     cre = m_pcre_compile(CUS list_ele, errstr);
305   return cre;
306 }
307
308 /*************************************************
309 *          Scan content for malware              *
310 *************************************************/
311
312 /* This is an internal interface for scanning an email; the normal interface
313 is via malware(), or there's malware_in_file() used for testing/debugging.
314
315 Arguments:
316   listptr       the list of options to the "malware = ..." ACL condition
317   eml_filename  the file holding the email to be scanned
318   faking        whether or not we're faking this up for the -bmalware test
319
320 Returns:        Exim message processing code (OK, FAIL, DEFER, ...)
321                 where true means malware was found (condition applies)
322 */
323 static int
324 malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking)
325 {
326   int sep = 0;
327   uschar *list = *listptr;
328   uschar *av_scanner_work = av_scanner;
329   uschar *scanner_name;
330   uschar *malware_regex;
331   uschar malware_regex_default[] = ".+";
332   unsigned long mbox_size;
333   FILE *mbox_file;
334   const pcre *re;
335   uschar * errstr;
336   struct scan * scanent;
337   const uschar * scanner_options;
338   int sock = -1;
339
340   /* make sure the eml mbox file is spooled up */
341   if (!(mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL)))
342     return malware_errlog_defer(US"error while creating mbox spool file");
343
344   /* none of our current scanners need the mbox
345      file as a stream, so we can close it right away */
346   (void)fclose(mbox_file);
347
348   /* extract the malware regex to match against from the option list */
349   if (!(malware_regex = string_nextinlist(&list, &sep, NULL, 0)))
350     return FAIL;                /* empty means "don't match anything" */
351
352   /* parse 1st option */
353     if ( (strcmpic(malware_regex,US"false") == 0) ||
354        (Ustrcmp(malware_regex,"0") == 0) )
355     return FAIL;                /* explicitly no matching */
356
357   /* special cases (match anything except empty) */
358   if ( (strcmpic(malware_regex,US"true") == 0) ||
359        (Ustrcmp(malware_regex,"*") == 0) ||
360        (Ustrcmp(malware_regex,"1") == 0) )
361     malware_regex = malware_regex_default;
362
363   /* Reset sep that is set by previous string_nextinlist() call */
364   sep = 0;
365
366   /* compile the regex, see if it works */
367   if (!(re = m_pcre_compile(malware_regex, &errstr)))
368     return malware_errlog_defer(errstr);
369
370   /* if av_scanner starts with a dollar, expand it first */
371   if (*av_scanner == '$') {
372     if (!(av_scanner_work = expand_string(av_scanner)))
373       return malware_errlog_defer(
374            string_sprintf("av_scanner starts with $, but expansion failed: %s",
375            expand_string_message));
376
377     debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
378     /* disable result caching in this case */
379     malware_name = NULL;
380     malware_ok = FALSE;
381   }
382
383   /* Do not scan twice (unless av_scanner is dynamic). */
384   if (!malware_ok) {
385
386     /* find the scanner type from the av_scanner option */
387     if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
388       return malware_errlog_defer(US"av_scanner configuration variable is empty");
389
390     for (scanent = m_scans; ; scanent++) {
391       if (!scanent->name)
392         return malware_errlog_defer(string_sprintf("unknown scanner type '%s'",
393           scanner_name));
394       if (strcmpic(scanner_name, US scanent->name) != 0)
395         continue;
396       if (!(scanner_options = string_nextinlist(&av_scanner_work, &sep, NULL, 0)))
397         scanner_options = scanent->options_default;
398       if (scanent->conn == MC_NONE)
399         break;
400       switch(scanent->conn)
401       {
402       case MC_TCP:  sock = m_tcpsocket_fromdef(scanner_options, &errstr); break;
403       case MC_UNIX: sock = m_unixsocket(scanner_options, &errstr);        break;
404       case MC_STRM: sock = m_streamsocket(scanner_options, &errstr);      break;
405       default: /* compiler quietening */ break;
406       }
407       if (sock < 0)
408         return m_errlog_defer(scanent, errstr);
409       break;
410     }
411     DEBUG(D_lookup) debug_printf("Malware scan: %s\n", scanner_name);
412
413     switch (scanent->scancode) {
414     case M_FPROTD: /* "f-protd" scanner type -------------------------------- */
415       {
416         uschar *fp_scan_option;
417         unsigned int detected=0, par_count=0;
418         uschar * scanrequest;
419         uschar buf[32768], *strhelper, *strhelper2;
420         uschar * malware_name_internal = NULL;
421
422         DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
423         scanrequest = string_sprintf("GET %s", eml_filename);
424
425         while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
426                               NULL, 0))) {
427           scanrequest = string_sprintf("%s%s%s", scanrequest,
428                                     par_count ? "%20" : "?", fp_scan_option);
429           par_count++;
430         }
431         scanrequest = string_sprintf("%s HTTP/1.0\r\n\r\n", scanrequest);
432
433         /* send scan request */
434         if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
435           return m_errlog_defer(scanent, errstr);
436
437         /* We get a lot of empty lines, so we need this hack to check for any data at all */
438         while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
439           if ( recv_line(sock, buf, sizeof(buf)) > 0) {
440             if ( Ustrstr(buf, US"<detected type=\"") != NULL )
441               detected = 1;
442             else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
443               if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
444                 *strhelper2 = '\0';
445                 malware_name_internal = string_copy(strhelper+6);
446               }
447             } else if ( Ustrstr(buf, US"<summary code=\"") )
448                 malware_name = Ustrstr(buf, US"<summary code=\"11\">")
449                   ? malware_name_internal : NULL;
450           }
451         }
452         break;
453       } /* f-protd */
454
455     case M_DRWEB: /* "drweb" scanner type ----------------------------------- */
456     /* v0.1 - added support for tcp sockets          */
457     /* v0.0 - initial release -- support for unix sockets      */
458       {
459         int result;
460         off_t fsize;
461         unsigned int fsize_uint;
462         uschar * tmpbuf, *drweb_fbuf;
463         int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
464             drweb_vnum, drweb_slen, drweb_fin = 0x0000;
465         unsigned long bread;
466         const pcre *drweb_re;
467
468         /* prepare variables */
469         drweb_cmd = htonl(DRWEBD_SCAN_CMD);
470         drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
471
472         if (*scanner_options != '/') {
473
474           /* calc file size */
475           if ((drweb_fd = open(CS eml_filename, O_RDONLY)) == -1)
476             return m_errlog_defer_3(scanent,
477               string_sprintf("can't open spool file %s: %s",
478                 eml_filename, strerror(errno)),
479               sock);
480
481           if ((fsize = lseek(drweb_fd, 0, SEEK_END)) == -1) {
482             int err = errno;
483             (void)close(drweb_fd);
484             return m_errlog_defer_3(scanent,
485               string_sprintf("can't seek spool file %s: %s",
486                 eml_filename, strerror(err)),
487               sock);
488           }
489           fsize_uint = (unsigned int) fsize;
490           if ((off_t)fsize_uint != fsize) {
491             (void)close(drweb_fd);
492             return m_errlog_defer_3(scanent,
493               string_sprintf("seeking spool file %s, size overflow",
494                 eml_filename),
495               sock);
496           }
497           drweb_slen = htonl(fsize);
498           lseek(drweb_fd, 0, SEEK_SET);
499
500           DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s]\n",
501               scanner_name, scanner_options);
502
503           /* send scan request */
504           if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
505               (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
506               (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
507               (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
508             (void)close(drweb_fd);
509             return m_errlog_defer_3(scanent,
510               string_sprintf("unable to send commands to socket (%s)", scanner_options),
511               sock);
512           }
513
514           if (!(drweb_fbuf = (uschar *) malloc (fsize_uint))) {
515             (void)close(drweb_fd);
516             return m_errlog_defer_3(scanent,
517               string_sprintf("unable to allocate memory %u for file (%s)",
518                 fsize_uint, eml_filename),
519               sock);
520           }
521
522           if ((result = read (drweb_fd, drweb_fbuf, fsize)) == -1) {
523             int err = errno;
524             (void)close(drweb_fd);
525             free(drweb_fbuf);
526             return m_errlog_defer_3(scanent,
527               string_sprintf("can't read spool file %s: %s",
528                 eml_filename, strerror(err)),
529               sock);
530           }
531           (void)close(drweb_fd);
532
533           /* send file body to socket */
534           if (send(sock, drweb_fbuf, fsize, 0) < 0) {
535             free(drweb_fbuf);
536             return m_errlog_defer_3(scanent,
537               string_sprintf("unable to send file body to socket (%s)", scanner_options),
538             sock);
539           }
540           (void)close(drweb_fd);
541
542         } else {
543
544           drweb_slen = htonl(Ustrlen(eml_filename));
545
546           DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
547               scanner_name, scanner_options);
548
549           /* send scan request */
550           if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
551               (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
552               (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
553               (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
554               (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0))
555             return m_errlog_defer_3(scanent,
556               string_sprintf("unable to send commands to socket (%s)", scanner_options),
557               sock);
558         }
559
560         /* wait for result */
561         if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc)))
562           return m_errlog_defer_3(scanent,
563                       US"unable to read return code", sock);
564         drweb_rc = ntohl(drweb_rc);
565
566         if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum)))
567           return m_errlog_defer_3(scanent,
568                               US"unable to read the number of viruses", sock);
569         drweb_vnum = ntohl(drweb_vnum);
570
571         /* "virus(es) found" if virus number is > 0 */
572         if (drweb_vnum) {
573           int i;
574
575           /* setup default virus name */
576           malware_name = US"unknown";
577
578           /* set up match regex */
579           drweb_re = m_pcre_compile(US"infected\\swith\\s*(.+?)$", &errstr);
580
581           /* read and concatenate virus names into one string */
582           for (i=0;i<drweb_vnum;i++)
583           {
584             int size = 0, off = 0, ovector[10*3];
585             /* read the size of report */
586             if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen)))
587               return m_errlog_defer_3(scanent,
588                                 US"cannot read report size", sock);
589             drweb_slen = ntohl(drweb_slen);
590             tmpbuf = store_get(drweb_slen);
591
592             /* read report body */
593             if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen)
594               return m_errlog_defer_3(scanent,
595                                 US"cannot read report string", sock);
596             tmpbuf[drweb_slen] = '\0';
597
598             /* try matcher on the line, grab substring */
599             result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0,
600                                     ovector, nelements(ovector));
601             if (result >= 2) {
602               const char * pre_malware_nb;
603
604               pcre_get_substring(CS tmpbuf, ovector, result, 1, &pre_malware_nb);
605
606               if (i==0) /* the first name we just copy to malware_name */
607                 malware_name = string_append(NULL, &size, &off,
608                                             1, pre_malware_nb);
609
610               else      /* concatenate each new virus name to previous */
611                 malware_name = string_append(malware_name, &size, &off,
612                                             2, "/", pre_malware_nb);
613
614               pcre_free_substring(pre_malware_nb);
615             }
616           }
617         }
618         else {
619           const char *drweb_s = NULL;
620
621           if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
622           if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
623           if (drweb_rc & DERR_TIMEOUT)  drweb_s = "timeout";
624           if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
625           /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
626            * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
627            * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
628            * and others are ignored */
629           if (drweb_s)
630             return m_errlog_defer_3(scanent,
631               string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s),
632               sock);
633
634           /* no virus found */
635           malware_name = NULL;
636         }
637         break;
638       } /* drweb */
639
640     case M_AVES: /* "aveserver" scanner type -------------------------------- */
641       {
642         uschar buf[32768];
643         int result;
644
645         /* read aveserver's greeting and see if it is ready (2xx greeting) */
646         recv_line(sock, buf, sizeof(buf));
647
648         if (buf[0] != '2')              /* aveserver is having problems */
649           return m_errlog_defer_3(scanent,
650             string_sprintf("unavailable (Responded: %s).",
651                             ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
652             sock);
653
654         /* prepare our command */
655         (void)string_format(buf, sizeof(buf), "SCAN bPQRSTUW %s\r\n",
656                                                   eml_filename);
657
658         DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
659
660         /* and send it */
661         if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0)
662           return m_errlog_defer(scanent, errstr);
663
664         malware_name = NULL;
665         result = 0;
666         /* read response lines, find malware name and final response */
667         while (recv_line(sock, buf, sizeof(buf)) > 0) {
668           debug_printf("aveserver: %s\n", buf);
669           if (buf[0] == '2')
670             break;
671           if (buf[0] == '5') {          /* aveserver is having problems */
672             result = m_errlog_defer(scanent,
673                string_sprintf("unable to scan file %s (Responded: %s).",
674                                eml_filename, buf));
675             break;
676           } else if (Ustrncmp(buf,"322",3) == 0) {
677             uschar *p = Ustrchr(&buf[4],' ');
678             *p = '\0';
679             malware_name = string_copy(&buf[4]);
680           }
681         }
682
683         /* and send it */
684         if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0)
685           return m_errlog_defer(scanent, errstr);
686
687         /* read aveserver's greeting and see if it is ready (2xx greeting) */
688         recv_line(sock, buf, sizeof(buf));
689
690         if (buf[0] != '2')              /* aveserver is having problems */
691           return m_errlog_defer_3(scanent,
692             string_sprintf("unable to quit dialogue (Responded: %s).",
693                           ((buf[0] != 0) ? buf : (uschar *)"nothing") ),
694             sock);
695
696         if (result == DEFER) {
697           (void)close(sock);
698           return DEFER;
699         }
700         break;
701       } /* aveserver */
702
703     case M_FSEC: /* "fsecure" scanner type ---------------------------------- */
704       {
705         int i, j, bread = 0;
706         uschar * file_name;
707         uschar av_buffer[1024];
708         const pcre * fs_inf;
709         static uschar *cmdopt[] = { US"CONFIGURE\tARCHIVE\t1\n",
710                                         US"CONFIGURE\tTIMEOUT\t0\n",
711                                         US"CONFIGURE\tMAXARCH\t5\n",
712                                         US"CONFIGURE\tMIME\t1\n" };
713         time_t tmo;
714
715         malware_name = NULL;
716
717         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
718             scanner_name, scanner_options);
719         tmo = time(NULL) + MALWARE_TIMEOUT;
720         /* pass options */
721         memset(av_buffer, 0, sizeof(av_buffer));
722         for (i=0; i != nelements(cmdopt); i++) {
723
724           if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
725             return m_errlog_defer(scanent, errstr);
726
727           bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
728           if (bread >0) av_buffer[bread]='\0';
729           if (bread < 0)
730             return m_errlog_defer_3(scanent,
731               string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
732               sock);
733           for (j=0;j<bread;j++)
734             if((av_buffer[j]=='\r')||(av_buffer[j]=='\n'))
735               av_buffer[j] ='@';
736         }
737
738         /* pass the mailfile to fsecure */
739         file_name = string_sprintf("SCAN\t%s\n", eml_filename);
740
741         if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
742           return m_errlog_defer(scanent, errstr);
743
744         /* set up match */
745         /* todo also SUSPICION\t */
746         fs_inf = m_pcre_compile(US"\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", &errstr);
747
748         /* read report, linewise. Apply a timeout as the Fsecure daemon
749         sometimes wants an answer to "PING" but they won't tell us what */
750         {
751           uschar * p = av_buffer;
752           uschar * q;
753
754           for (;;)
755             {
756             int t = tmo - time(NULL);
757
758             errno = ETIME;
759             i =  av_buffer+sizeof(av_buffer)-p;
760             if (  t <= 0
761                || (bread= ip_recv(sock, p, i-1, t)) < 0
762                )
763               return m_errlog_defer_3(scanent,
764                 string_sprintf("unable to read result (%s)", strerror(errno)),
765                 sock);
766
767             for (p[bread] = '\0'; q = strchr(p, '\n'); p = q+1)
768               {
769               *q = '\0';
770
771               /* Really search for virus again? */
772               if (!malware_name)
773                 /* try matcher on the line, grab substring */
774                 malware_name = m_pcre_exec(fs_inf, p);
775
776               if (Ustrstr(p, "OK\tScan ok."))
777                 goto fsec_found;
778               }
779
780             /* copy down the trailing partial line then read another chunk */
781             i =  av_buffer+sizeof(av_buffer)-p;
782             memmove(av_buffer, p, i);
783             p = av_buffer+i;
784           }
785         }
786
787       fsec_found:
788         break;
789       } /* fsecure */
790
791     case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
792       {
793         time_t t;
794         uschar tmpbuf[1024];
795         uschar * scanrequest;
796         int kav_rc;
797         unsigned long kav_reportlen, bread;
798         const pcre *kav_re;
799         uschar *p;
800
801         /* get current date and time, build scan request */
802         time(&t);
803         /* pdp note: before the eml_filename parameter, this scanned the
804         directory; not finding documentation, so we'll strip off the directory.
805         The side-effect is that the test framework scanning may end up in
806         scanning more than was requested, but for the normal interface, this is
807         fine. */
808
809         strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
810         scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
811         p = Ustrrchr(scanrequest, '/');
812         if (p)
813           *p = '\0';
814
815         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
816             scanner_name, scanner_options);
817
818         /* send scan request */
819         if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
820           return m_errlog_defer(scanent, errstr);
821
822         /* wait for result */
823         if ((bread = recv(sock, tmpbuf, 2, 0) != 2))
824           return m_errlog_defer_3(scanent,
825                               US"unable to read 2 bytes from socket.", sock);
826
827         /* get errorcode from one nibble */
828         kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
829         switch(kav_rc)
830         {
831         case 5: case 6: /* improper kavdaemon configuration */
832           return m_errlog_defer_3(scanent,
833                   US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
834                   sock);
835         case 1:
836           return m_errlog_defer_3(scanent,
837                   US"reported 'scanning not completed' (code 1).", sock);
838         case 7:
839           return m_errlog_defer_3(scanent,
840                   US"reported 'kavdaemon damaged' (code 7).", sock);
841         }
842
843         /* code 8 is not handled, since it is ambigous. It appears mostly on
844         bounces where part of a file has been cut off */
845
846         /* "virus found" return codes (2-4) */
847         if ((kav_rc > 1) && (kav_rc < 5)) {
848           int report_flag = 0;
849
850           /* setup default virus name */
851           malware_name = US"unknown";
852
853           report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
854
855           /* read the report, if available */
856           if( report_flag == 1 ) {
857             /* read report size */
858             if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4)
859               return m_errlog_defer_3(scanent,
860                     US"cannot read report size", sock);
861
862             /* it's possible that avp returns av_buffer[1] == 1 but the
863             reportsize is 0 (!?) */
864             if (kav_reportlen > 0) {
865               /* set up match regex, depends on retcode */
866               kav_re = m_pcre_compile( kav_rc == 3
867                                        ? US"suspicion:\\s*(.+?)\\s*$"
868                                        : US"infected:\\s*(.+?)\\s*$",
869                                        &errstr );
870
871               /* read report, linewise */
872               while (kav_reportlen > 0) {
873                 bread = 0;
874                 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
875                   kav_reportlen--;
876                   if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
877                   bread++;
878                 }
879                 bread++;
880                 tmpbuf[bread] = '\0';
881
882                 /* try matcher on the line, grab substring */
883                 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
884                   break;
885               }
886             }
887           }
888         }
889         else /* no virus found */
890           malware_name = NULL;
891
892         break;
893       }
894
895     case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
896       {
897         const uschar *cmdline_scanner = scanner_options;
898         const pcre *cmdline_trigger_re;
899         const pcre *cmdline_regex_re;
900         uschar * file_name;
901         uschar * commandline;
902         void (*eximsigchld)(int);
903         void (*eximsigpipe)(int);
904         FILE *scanner_out = NULL;
905         FILE *scanner_record = NULL;
906         uschar linebuffer[32767];
907         int trigger = 0;
908         uschar *p;
909
910         if (!cmdline_scanner)
911           return m_errlog_defer(scanent, errstr);
912
913         /* find scanner output trigger */
914         cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
915                                   "missing trigger specification", &errstr);
916         if (!cmdline_trigger_re)
917           return m_errlog_defer(scanent, errstr);
918
919         /* find scanner name regex */
920         cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
921                             "missing virus name regex specification", &errstr);
922         if (!cmdline_regex_re)
923           return m_errlog_defer(scanent, errstr);
924
925         /* prepare scanner call; despite the naming, file_name holds a directory
926         name which is documented as the value given to %s. */
927
928         file_name = string_copy(eml_filename);
929         p = Ustrrchr(file_name, '/');
930         if (p)
931           *p = '\0';
932         commandline = string_sprintf(CS cmdline_scanner, file_name);
933
934         /* redirect STDERR too */
935         commandline = string_sprintf("%s 2>&1", commandline);
936
937         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
938
939         /* store exims signal handlers */
940         eximsigchld = signal(SIGCHLD,SIG_DFL);
941         eximsigpipe = signal(SIGPIPE,SIG_DFL);
942
943         if (!(scanner_out = popen(CS commandline,"r"))) {
944           int err = errno;
945           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
946           return m_errlog_defer(scanent,
947             string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
948         }
949
950         file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
951                                   spool_directory, message_id, message_id);
952         scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
953
954         if (scanner_record == NULL) {
955           int err = errno;
956           (void) pclose(scanner_out);
957           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
958           return m_errlog_defer(scanent,
959             string_sprintf("opening scanner output file (%s) failed: %s.",
960               file_name, strerror(err)));
961         }
962
963         /* look for trigger while recording output */
964         while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out)) {
965           if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
966             /* short write */
967             (void) pclose(scanner_out);
968             signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
969             return m_errlog_defer(scanent,
970               string_sprintf("short write on scanner output file (%s).", file_name));
971           }
972           /* try trigger match */
973           if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
974             trigger = 1;
975         }
976
977         (void)fclose(scanner_record);
978         sep = pclose(scanner_out);
979         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
980         if (sep != 0)
981             return m_errlog_defer(scanent,
982                 sep == -1
983                 ? string_sprintf("running scanner failed: %s", strerror(sep))
984                 : string_sprintf("scanner returned error code: %d", sep));
985
986         if (trigger) {
987           uschar * s;
988           /* setup default virus name */
989           malware_name = US"unknown";
990
991           /* re-open the scanner output file, look for name match */
992           scanner_record = fopen(CS file_name, "rb");
993           while(fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) {
994             /* try match */
995             if ((s = m_pcre_exec(cmdline_regex_re, linebuffer)))
996               malware_name = s;
997           }
998           (void)fclose(scanner_record);
999         }
1000         else /* no virus found */
1001           malware_name = NULL;
1002         break;
1003       } /* cmdline */
1004
1005     case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
1006       {
1007         int bread = 0;
1008         uschar *p;
1009         uschar * file_name;
1010         uschar av_buffer[1024];
1011
1012         /* pass the scan directory to sophie */
1013         file_name = string_copy(eml_filename);
1014         if ((p = Ustrrchr(file_name, '/')))
1015           *p = '\0';
1016
1017         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1018             scanner_name, scanner_options);
1019
1020         if (  write(sock, file_name, Ustrlen(file_name)) < 0
1021            || write(sock, "\n", 1) != 1
1022            )
1023           return m_errlog_defer_3(scanent,
1024             string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1025             sock);
1026
1027         /* wait for result */
1028         memset(av_buffer, 0, sizeof(av_buffer));
1029         if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0))
1030           return m_errlog_defer_3(scanent,
1031             string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1032             sock);
1033
1034         /* infected ? */
1035         if (av_buffer[0] == '1') {
1036           uschar * s = Ustrchr(av_buffer, '\n');
1037           if (s)
1038             *s = '\0';
1039           malware_name = string_copy(&av_buffer[2]);
1040         }
1041         else if (!strncmp(CS av_buffer, "-1", 2))
1042           return m_errlog_defer_3(scanent, US"scanner reported error", sock);
1043         else /* all ok, no virus */
1044           malware_name = NULL;
1045
1046         break;
1047       }
1048
1049     case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1050       {
1051       /* This code was originally contributed by David Saez */
1052       /* There are three scanning methods available to us:
1053        *  (1) Use the SCAN command, pointing to a file in the filesystem
1054        *  (2) Use the STREAM command, send the data on a separate port
1055        *  (3) Use the zINSTREAM command, send the data inline
1056        * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1057        * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1058        * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1059        * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1060        * WITH_OLD_CLAMAV_STREAM is defined.
1061        * See Exim bug 926 for details.  */
1062
1063         uschar *p, *vname, *result_tag, *response_end;
1064         int bread=0;
1065         uschar * file_name;
1066         uschar av_buffer[1024];
1067         uschar *hostname = US"";
1068         host_item connhost;
1069         uschar *clamav_fbuf;
1070         int clam_fd, result;
1071         off_t fsize;
1072         unsigned int fsize_uint;
1073         BOOL use_scan_command = FALSE;
1074         clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1075         int current_server;
1076         int num_servers = 0;
1077   #ifdef WITH_OLD_CLAMAV_STREAM
1078         unsigned int port;
1079         uschar av_buffer2[1024];
1080         int sockData;
1081   #else
1082         uint32_t send_size, send_final_zeroblock;
1083   #endif
1084
1085         if (*scanner_options == '/')
1086           /* Local file; so we def want to use_scan_command and don't want to try
1087            * passing IP/port combinations */
1088           use_scan_command = TRUE;
1089         else {
1090           const uschar *address = scanner_options;
1091           uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1092
1093           /* Go through the rest of the list of host/port and construct an array
1094            * of servers to try. The first one is the bit we just passed from
1095            * scanner_options so process that first and then scan the remainder of
1096            * the address buffer */
1097           do {
1098             clamd_address_container *this_clamd;
1099
1100             /* The 'local' option means use the SCAN command over the network
1101              * socket (ie common file storage in use) */
1102             if (strcmpic(address,US"local") == 0) {
1103               use_scan_command = TRUE;
1104               continue;
1105             }
1106
1107             /* XXX: If unsuccessful we should free this memory */
1108             this_clamd =
1109                 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1110
1111             /* extract host and port part */
1112             if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u",
1113                    this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) {
1114               (void) m_errlog_defer(scanent,
1115                           string_sprintf("invalid address '%s'", address));
1116               continue;
1117             }
1118
1119             clamd_address_vector[num_servers] = this_clamd;
1120             num_servers++;
1121             if (num_servers >= MAX_CLAMD_SERVERS) {
1122               (void) m_errlog_defer(scanent,
1123                     US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1124                     "specified; only using the first " MAX_CLAMD_SERVERS_S );
1125               break;
1126             }
1127           } while ((address = string_nextinlist(&av_scanner_work, &sep,
1128                                           address_buffer,
1129                                           sizeof(address_buffer))) != NULL);
1130
1131           /* check if we have at least one server */
1132           if (!num_servers)
1133             return m_errlog_defer(scanent,
1134               US"no useable server addresses in malware configuration option.");
1135         }
1136
1137         /* See the discussion of response formats below to see why we really don't
1138         like colons in filenames when passing filenames to ClamAV. */
1139         if (use_scan_command && Ustrchr(eml_filename, ':'))
1140           return m_errlog_defer(scanent,
1141             string_sprintf("local/SCAN mode incompatible with" \
1142               " : in path to email filename [%s]", eml_filename));
1143
1144         /* We have some network servers specified */
1145         if (num_servers) {
1146
1147           /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1148            * only supports AF_INET, but we should probably be looking to the
1149            * future and rewriting this to be protocol-independent anyway. */
1150
1151           while ( num_servers > 0 ) {
1152             /* Randomly pick a server to start with */
1153             current_server = random_number( num_servers );
1154
1155             debug_printf("trying server name %s, port %u\n",
1156                          clamd_address_vector[current_server]->tcp_addr,
1157                          clamd_address_vector[current_server]->tcp_port);
1158
1159             /* Lookup the host. This is to ensure that we connect to the same IP
1160              * on both connections (as one host could resolve to multiple ips) */
1161             sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr,
1162                                 clamd_address_vector[current_server]->tcp_port,
1163                                 &connhost, &errstr);
1164             if (sock >= 0) {
1165               /* Connection successfully established with a server */
1166               hostname = clamd_address_vector[current_server]->tcp_addr;
1167               break;
1168             }
1169
1170             (void) m_errlog_defer(scanent, errstr);
1171
1172             /* Remove the server from the list. XXX We should free the memory */
1173             num_servers--;
1174             int i;
1175             for( i = current_server; i < num_servers; i++ )
1176               clamd_address_vector[i] = clamd_address_vector[i+1];
1177           }
1178
1179           if ( num_servers == 0 )
1180             return m_errlog_defer(scanent, US"all servers failed");
1181
1182         } else {
1183           if ((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1184             return m_errlog_defer(scanent, errstr);
1185         }
1186
1187         /* have socket in variable "sock"; command to use is semi-independent of
1188          * the socket protocol.  We use SCAN if is local (either Unix/local
1189          * domain socket, or explicitly told local) else we stream the data.
1190          * How we stream the data depends upon how we were built.  */
1191
1192         if (!use_scan_command) {
1193
1194   #ifdef WITH_OLD_CLAMAV_STREAM
1195           /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1196            * that port on a second connection; then in the scan-method-neutral
1197            * part, read the response back on the original connection. */
1198
1199           DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1200               scanner_name);
1201
1202           /* Pass the string to ClamAV (7 = "STREAM\n") */
1203           if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1204             return m_errlog_defer(scanent, errstr);
1205
1206           memset(av_buffer2, 0, sizeof(av_buffer2));
1207           bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1208
1209           if (bread < 0)
1210             return m_errlog_defer_3(scanent,
1211               string_sprintf("unable to read PORT from socket (%s)",
1212                   strerror(errno)),
1213               sock);
1214
1215           if (bread == sizeof(av_buffer2))
1216             return m_errlog_defer_3(scanent, "buffer too small", sock);
1217
1218           if (!(*av_buffer2))
1219             return m_errlog_defer_3(scanent, "ClamAV returned null", sock);
1220
1221           av_buffer2[bread] = '\0';
1222           if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
1223             return m_errlog_defer_3(scanent,
1224               string_sprintf("Expected port information from clamd, got '%s'",
1225                 av_buffer2),
1226               sock);
1227
1228           sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
1229           if (sockData < 0)
1230             return m_errlog_defer_3(scanent, errstr, sock);
1231
1232   #define CLOSE_SOCKDATA (void)close(sockData)
1233   #else /* WITH_OLD_CLAMAV_STREAM not defined */
1234           /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1235           chunks, <n> a 4-byte number (network order), terminated by a zero-length
1236           chunk. */
1237
1238           DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1239               scanner_name);
1240
1241           /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1242           if (send(sock, "zINSTREAM", 10, 0) < 0)
1243             return m_errlog_defer_3(scanent,
1244               string_sprintf("unable to send zINSTREAM to socket (%s)",
1245                 strerror(errno)),
1246               sock);
1247
1248   #define CLOSE_SOCKDATA /**/
1249   #endif
1250
1251           /* calc file size */
1252           if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) {
1253             int err = errno;
1254             CLOSE_SOCKDATA;
1255             return m_errlog_defer_3(scanent,
1256               string_sprintf("can't open spool file %s: %s",
1257                 eml_filename, strerror(err)),
1258               sock);
1259           }
1260           if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) {
1261             int err = errno;
1262             CLOSE_SOCKDATA; (void)close(clam_fd);
1263             return m_errlog_defer_3(scanent,
1264               string_sprintf("can't seek spool file %s: %s",
1265                 eml_filename, strerror(err)),
1266               sock);
1267           }
1268           fsize_uint = (unsigned int) fsize;
1269           if ((off_t)fsize_uint != fsize) {
1270             CLOSE_SOCKDATA; (void)close(clam_fd);
1271             return m_errlog_defer_3(scanent,
1272               string_sprintf("seeking spool file %s, size overflow",
1273                 eml_filename),
1274               sock);
1275           }
1276           lseek(clam_fd, 0, SEEK_SET);
1277
1278           if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) {
1279             CLOSE_SOCKDATA; (void)close(clam_fd);
1280             return m_errlog_defer_3(scanent,
1281               string_sprintf("unable to allocate memory %u for file (%s)",
1282                 fsize_uint, eml_filename),
1283               sock);
1284           }
1285
1286           if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) {
1287             int err = errno;
1288             free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd);
1289             return m_errlog_defer_3(scanent,
1290               string_sprintf("can't read spool file %s: %s",
1291                 eml_filename, strerror(err)),
1292               sock);
1293           }
1294           (void)close(clam_fd);
1295
1296           /* send file body to socket */
1297   #ifdef WITH_OLD_CLAMAV_STREAM
1298           if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) {
1299             free(clamav_fbuf); CLOSE_SOCKDATA;
1300             return m_errlog_defer_3(scanent,
1301               string_sprintf("unable to send file body to socket (%s:%u)",
1302                 hostname, port),
1303               sock);
1304           }
1305   #else
1306           send_size = htonl(fsize_uint);
1307           send_final_zeroblock = 0;
1308           if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1309               (send(sock, clamav_fbuf, fsize_uint, 0) < 0) ||
1310               (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1311             {
1312             free(clamav_fbuf);
1313             return m_errlog_defer_3(scanent,
1314               string_sprintf("unable to send file body to socket (%s)", hostname),
1315               sock);
1316             }
1317   #endif
1318
1319           free(clamav_fbuf);
1320
1321           CLOSE_SOCKDATA;
1322   #undef CLOSE_SOCKDATA
1323
1324         } else { /* use scan command */
1325           /* Send a SCAN command pointing to a filename; then in the then in the
1326            * scan-method-neutral part, read the response back */
1327
1328   /* ================================================================= */
1329
1330           /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1331           which dates to when ClamAV needed us to break apart the email into the
1332           MIME parts (eg, with the now deprecated demime condition coming first).
1333           Some time back, ClamAV gained the ability to deconstruct the emails, so
1334           doing this would actually have resulted in the mail attachments being
1335           scanned twice, in the broken out files and from the original .eml.
1336           Since ClamAV now handles emails (and has for quite some time) we can
1337           just use the email file itself. */
1338           /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1339           file_name = string_sprintf("SCAN %s\n", eml_filename);
1340
1341           DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1342               scanner_name, scanner_options);
1343
1344           if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
1345             return m_errlog_defer_3(scanent,
1346               string_sprintf("unable to write to socket (%s)", strerror(errno)),
1347               sock);
1348
1349           /* Do not shut down the socket for writing; a user report noted that
1350            * clamd 0.70 does not react well to this. */
1351         }
1352         /* Commands have been sent, no matter which scan method or connection
1353          * type we're using; now just read the result, independent of method. */
1354
1355         /* Read the result */
1356         memset(av_buffer, 0, sizeof(av_buffer));
1357         bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1358         (void)close(sock);
1359         sock = -1;
1360
1361         if (!(bread > 0))
1362           return m_errlog_defer(scanent,
1363             string_sprintf("unable to read from socket (%s)", strerror(errno)));
1364
1365         if (bread == sizeof(av_buffer))
1366           return m_errlog_defer(scanent, US"buffer too small");
1367         /* We're now assured of a NULL at the end of av_buffer */
1368
1369         /* Check the result. ClamAV returns one of two result formats.
1370         In the basic mode, the response is of the form:
1371           infected: -> "<filename>: <virusname> FOUND"
1372           not-infected: -> "<filename>: OK"
1373           error: -> "<filename>: <errcode> ERROR
1374         If the ExtendedDetectionInfo option has been turned on, then we get:
1375           "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1376         for the infected case.  Compare:
1377   /tmp/eicar.com: Eicar-Test-Signature FOUND
1378   /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1379
1380         In the streaming case, clamd uses the filename "stream" which you should
1381         be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1382         client app will replace "stream" with the original filename before returning
1383         results to stdout, but the trace shows the data).
1384
1385         We will assume that the pathname passed to clamd from Exim does not contain
1386         a colon.  We will have whined loudly above if the eml_filename does (and we're
1387         passing a filename to clamd). */
1388
1389         if (!(*av_buffer))
1390           return m_errlog_defer(scanent, US"ClamAV returned null");
1391
1392         /* strip newline at the end (won't be present for zINSTREAM)
1393         (also any trailing whitespace, which shouldn't exist, but we depend upon
1394         this below, so double-check) */
1395         p = av_buffer + Ustrlen(av_buffer) - 1;
1396         if (*p == '\n') *p = '\0';
1397
1398         DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1399
1400         while (isspace(*--p) && (p > av_buffer))
1401           *p = '\0';
1402         if (*p) ++p;
1403         response_end = p;
1404
1405         /* colon in returned output? */
1406         if((p = Ustrchr(av_buffer,':')) == NULL)
1407           return m_errlog_defer(scanent,
1408             string_sprintf("ClamAV returned malformed result (missing colon): %s",
1409                     av_buffer));
1410
1411         /* strip filename */
1412         while (*p && isspace(*++p)) /**/;
1413         vname = p;
1414
1415         /* It would be bad to encounter a virus with "FOUND" in part of the name,
1416         but we should at least be resistant to it. */
1417         p = Ustrrchr(vname, ' ');
1418         result_tag = p ? p+1 : vname;
1419
1420         if (Ustrcmp(result_tag, "FOUND") == 0) {
1421           /* p should still be the whitespace before the result_tag */
1422           while (isspace(*p)) --p;
1423           *++p = '\0';
1424           /* Strip off the extended information too, which will be in parens
1425           after the virus name, with no intervening whitespace. */
1426           if (*--p == ')') {
1427             /* "(hash:size)", so previous '(' will do; if not found, we have
1428             a curious virus name, but not an error. */
1429             p = Ustrrchr(vname, '(');
1430             if (p)
1431               *p = '\0';
1432           }
1433           malware_name = string_copy(vname);
1434           DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1435
1436         } else if (Ustrcmp(result_tag, "ERROR") == 0)
1437           return m_errlog_defer(scanent,
1438             string_sprintf("ClamAV returned: %s", av_buffer));
1439
1440         else if (Ustrcmp(result_tag, "OK") == 0) {
1441           /* Everything should be OK */
1442           malware_name = NULL;
1443           DEBUG(D_acl) debug_printf("Malware not found\n");
1444
1445         } else
1446           return m_errlog_defer(scanent,
1447             string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1448
1449         break;
1450       } /* clamd */
1451
1452     case M_SOCK: /* "sock" scanner type ------------------------------------- */
1453       /* This code was derived by Martin Poole from the clamd code contributed
1454          by David Saez and the cmdline code
1455       */
1456       {
1457         int bread;
1458         uschar * commandline;
1459         uschar av_buffer[1024];
1460         uschar * linebuffer;
1461         uschar * sockline_scanner;
1462         uschar sockline_scanner_default[] = "%s\n";
1463         const pcre *sockline_trig_re;
1464         const pcre *sockline_name_re;
1465
1466         /* find scanner command line */
1467         if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1468                                             NULL, 0)))
1469         {       /* check for no expansions apart from one %s */
1470           uschar * s = Ustrchr(sockline_scanner, '%');
1471           if (s++)
1472             if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1473               return m_errlog_defer_3(scanent,
1474                                     US"unsafe sock scanner call spec", sock);
1475         }
1476         else
1477           sockline_scanner = sockline_scanner_default;
1478
1479         /* find scanner output trigger */
1480         sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1481                                   "missing trigger specification", &errstr);
1482         if (!sockline_trig_re)
1483           return m_errlog_defer_3(scanent, errstr, sock);
1484
1485         /* find virus name regex */
1486         sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1487                             "missing virus name regex specification", &errstr);
1488         if (!sockline_name_re)
1489           return m_errlog_defer_3(scanent, errstr, sock);
1490
1491         /* prepare scanner call - security depends on expansions check above */
1492         commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
1493         commandline = string_sprintf( CS sockline_scanner, CS commandline);
1494
1495
1496         /* Pass the command string to the socket */
1497         if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
1498           return m_errlog_defer(scanent, errstr);
1499
1500         /* Read the result */
1501         memset(av_buffer, 0, sizeof(av_buffer));
1502         bread = read(sock, av_buffer, sizeof(av_buffer));
1503
1504         if (!(bread > 0))
1505           return m_errlog_defer_3(scanent,
1506             string_sprintf("unable to read from socket (%s)", strerror(errno)),
1507             sock);
1508
1509         if (bread == sizeof(av_buffer))
1510           return m_errlog_defer_3(scanent, US"buffer too small", sock);
1511         linebuffer = string_copy(av_buffer);
1512
1513         /* try trigger match */
1514         if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) {
1515           if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1516             malware_name = US "unknown";
1517         }
1518         else /* no virus found */
1519           malware_name = NULL;
1520         break;
1521       }
1522
1523     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1524       {
1525         char *mksd_options_end;
1526         int mksd_maxproc = 1;  /* default, if no option supplied */
1527         int sock;
1528         int retval;
1529
1530         if (scanner_options) {
1531           mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1532           if (  *scanner_options == '\0'
1533              || *mksd_options_end != '\0'
1534              || mksd_maxproc < 1
1535              || mksd_maxproc > 32
1536              )
1537             return m_errlog_defer(scanent,
1538               string_sprintf("invalid option '%s'", scanner_options));
1539         }
1540
1541         if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1542           return m_errlog_defer(scanent, errstr);
1543
1544         malware_name = NULL;
1545
1546         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1547
1548         if ((retval = mksd_scan_packed(scanent, sock, eml_filename)) != OK) {
1549           close (sock);
1550           return retval;
1551         }
1552         break;
1553       }
1554     case M_AVAST: /* "avast" scanner type ----------------------------------- */
1555       {
1556       int ovector[1*3];
1557       uschar buf[1024];
1558       uschar * scanrequest;
1559       const pcre * avast_clean_re, * avast_virus_re;
1560       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1561
1562       /* According to Martin Tuma @avast the protocol uses "escaped
1563       whitespace", that is, every embedded whitespace is backslash
1564       escaped, as well as backslash is protected by backslash.
1565       The returned lines contain the name of the scanned file, a tab
1566       and the [ ] marker.
1567       [+] - not infected
1568       [L] - infected
1569       [E] - some error occured
1570       Such marker follows the first non-escaped TAB.  */
1571       if (  !(avast_clean_re =
1572                 m_pcre_compile(US"(?!\\\\)\\t\\[\\+\\]", &errstr))
1573          || !(avast_virus_re =
1574                 m_pcre_compile(US"(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)",
1575                   &errstr))
1576          )
1577         return malware_errlog_defer(errstr);
1578
1579       /* wait for result */
1580       for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf)) > 0; )
1581         {
1582         int slen = Ustrlen(buf);
1583         if (slen >= 1) 
1584           {
1585           DEBUG(D_acl) debug_printf("got from avast: %s\n", buf);
1586           switch (avast_stage)
1587             {
1588             case AVA_HELO:
1589               if (Ustrncmp(buf, "220", 3) != 0)
1590                 goto endloop;                   /* require a 220 */
1591               goto sendreq;
1592
1593             case AVA_OPT:
1594               if (Ustrncmp(buf, "210", 3) == 0)
1595                 break;                          /* ignore 210 responses */
1596               if (Ustrncmp(buf, "200", 3) != 0)
1597                 goto endloop;                   /* require a 200 */
1598
1599             sendreq:
1600               {
1601               int len;
1602               /* Check for another option to send. Newline-terminate it. */
1603               if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
1604                                 NULL, 0)))
1605                 {
1606                 scanrequest = string_sprintf("%s\n", scanrequest);
1607                 avast_stage = AVA_OPT;          /* just sent option */
1608                 }
1609               else
1610                 {
1611                 scanrequest = string_sprintf("SCAN %s/scan/%s\n",
1612                     spool_directory, message_id);
1613                 avast_stage = AVA_RSP;          /* just sent command */
1614                 }
1615
1616               /* send config-cmd or scan-request to socket */
1617               len = Ustrlen(scanrequest);
1618               if (send(sock, scanrequest, len, 0) < 0)
1619                 {
1620                 scanrequest[len-1] = '\0';
1621                 return m_errlog_defer_3(scanent, string_sprintf(
1622                       "unable to send request '%s' to socket (%s): %s",
1623                       scanrequest, scanner_options, strerror(errno)), sock);
1624                 }
1625               break;
1626               }
1627
1628             case AVA_RSP:
1629               if (Ustrncmp(buf, "210", 3) == 0)
1630                 break;  /* ignore the "210 SCAN DATA" message */
1631
1632               if (pcre_exec(avast_clean_re, NULL, CS buf, slen,
1633                     0, 0, ovector, nelements(ovector)) > 0)
1634                 break;
1635
1636               if ((malware_name = m_pcre_exec(avast_virus_re, buf)))
1637                 { /* remove backslash in front of [whitespace|backslash] */
1638                 uschar * p, * p0;
1639                 for (p = malware_name; *p; ++p) 
1640                   if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
1641                     for (p0 = p; *p0; ++p0) *p0 = p0[1];
1642                 
1643                 avast_stage = AVA_DONE;
1644                 goto endloop;
1645                 }
1646
1647               if (Ustrncmp(buf, "200 SCAN OK", 11) == 0) 
1648                 { /* we're done finally */
1649                 if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */
1650                   return m_errlog_defer_3(scanent, string_sprintf(
1651                               "unable to send quit request to socket (%s): %s",
1652                               scanner_options, strerror(errno)),
1653                               sock);
1654                 malware_name = NULL;
1655                 avast_stage = AVA_DONE;
1656                 goto endloop;
1657                 }
1658
1659               /* here for any unexpected response from the scanner */
1660               goto endloop;
1661             }
1662         }
1663       }
1664       endloop:
1665
1666       switch(avast_stage)
1667         {
1668         case AVA_HELO:  
1669         case AVA_OPT:
1670         case AVA_RSP:   return m_errlog_defer_3(scanent, string_sprintf(
1671                           "invalid response from scanner: %s\n", buf), sock);
1672         default:        break;
1673         }
1674       }
1675     }   /* scanner type switch */
1676
1677     if (sock >= 0)
1678       (void) close (sock);
1679     malware_ok = TRUE;                  /* set "been here, done that" marker */
1680   }
1681
1682   /* match virus name against pattern (caseless ------->----------v) */
1683   if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
1684     {
1685     DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1686     return OK;
1687     }
1688   else
1689     return FAIL;
1690 }
1691
1692
1693 /* simple wrapper for reading lines from sockets */
1694 int
1695 recv_line(int sock, uschar *buffer, int size)
1696 {
1697   uschar *p = buffer;
1698
1699   memset(buffer,0,size);
1700   /* read until \n */
1701   while(recv(sock,p,1,0) > -1) {
1702     if ((p-buffer) > (size-2)) break;
1703     if (*p == '\n') break;
1704     if (*p != '\r') p++;
1705   }
1706   *p = '\0';
1707
1708   return (p-buffer);
1709 }
1710
1711
1712 /* ============= private routines for the "mksd" scanner type ============== */
1713
1714 #include <sys/uio.h>
1715
1716 static inline int
1717 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1718 {
1719   int i;
1720
1721   for (;;) {
1722     do
1723       i = writev (sock, iov, iovcnt);
1724     while ((i < 0) && (errno == EINTR));
1725     if (i <= 0) {
1726       (void) malware_errlog_defer(US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1727       return -1;
1728     }
1729
1730     for (;;)
1731       if (i >= iov->iov_len) {
1732         if (--iovcnt == 0)
1733           return 0;
1734         i -= iov->iov_len;
1735         iov++;
1736       } else {
1737         iov->iov_len -= i;
1738         iov->iov_base = CS iov->iov_base + i;
1739         break;
1740       }
1741   }
1742 }
1743
1744 static inline int
1745 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1746 {
1747   int offset = 0;
1748   int i;
1749
1750   do {
1751     if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1752       (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1753       return -1;
1754     }
1755
1756     offset += i;
1757     /* offset == av_buffer_size -> buffer full */
1758     if (offset == av_buffer_size) {
1759       (void) malware_errlog_defer(US"malformed reply received from mksd");
1760       return -1;
1761     }
1762   } while (av_buffer[offset-1] != '\n');
1763
1764   av_buffer[offset] = '\0';
1765   return offset;
1766 }
1767
1768 static inline int
1769 mksd_parse_line(struct scan * scanent, char *line)
1770 {
1771   char *p;
1772
1773   switch (*line) {
1774     case 'O': /* OK */
1775       return OK;
1776
1777     case 'E':
1778     case 'A': /* ERR */
1779       if ((p = strchr (line, '\n')) != NULL)
1780         *p = '\0';
1781       return m_errlog_defer(scanent,
1782         string_sprintf("scanner failed: %s", line));
1783
1784     default: /* VIR */
1785       if ((p = strchr (line, '\n')) != NULL) {
1786         *p = '\0';
1787         if (((p-line) > 5) && (line[3] == ' '))
1788           if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1789             *p = '\0';
1790             malware_name = string_copy(US line+4);
1791             return OK;
1792           }
1793       }
1794       return m_errlog_defer(scanent,
1795         string_sprintf("malformed reply received: %s", line));
1796   }
1797 }
1798
1799 static int
1800 mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename)
1801 {
1802   struct iovec iov[3];
1803   const char *cmd = "MSQ\n";
1804   uschar av_buffer[1024];
1805
1806   iov[0].iov_base = (void *) cmd;
1807   iov[0].iov_len = 3;
1808   iov[1].iov_base = CS scan_filename;
1809   iov[1].iov_len = Ustrlen(scan_filename);
1810   iov[2].iov_base = (void *) (cmd + 3);
1811   iov[2].iov_len = 1;
1812
1813   if (mksd_writev (sock, iov, 3) < 0)
1814     return DEFER;
1815
1816   if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1817     return DEFER;
1818
1819   return mksd_parse_line (scanent, CS av_buffer);
1820 }
1821
1822 #endif /*WITH_CONTENT_SCAN*/
1823 /*
1824  * vi: aw ai sw=2
1825  */