Add support for avast malware scanner. Bug 1033
[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
714         malware_name = NULL;
715
716         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
717             scanner_name, scanner_options);
718
719         /* pass options */
720         memset(av_buffer, 0, sizeof(av_buffer));
721         for (i=0; i != nelements(cmdopt); i++) {
722
723           if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0)
724             return m_errlog_defer(scanent, errstr);
725
726           bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
727           if (bread >0) av_buffer[bread]='\0';
728           if (bread < 0)
729             return m_errlog_defer_3(scanent,
730               string_sprintf("unable to read answer %d (%s)", i, strerror(errno)),
731               sock);
732           for (j=0;j<bread;j++)
733             if((av_buffer[j]=='\r')||(av_buffer[j]=='\n'))
734               av_buffer[j] ='@';
735         }
736
737         /* pass the mailfile to fsecure */
738         file_name = string_sprintf("SCAN\t%s\n", eml_filename);
739
740         if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0)
741           return m_errlog_defer(scanent, errstr);
742
743         /* set up match */
744         /* todo also SUSPICION\t */
745         fs_inf = m_pcre_compile(US"\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", &errstr);
746
747         /* read report, linewise */
748         do {
749           i = 0;
750           memset(av_buffer, 0, sizeof(av_buffer));
751           do {
752             if ((bread= ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT)) < 0)
753               return m_errlog_defer_3(scanent,
754                 string_sprintf("unable to read result (%s)", strerror(errno)),
755                 sock);
756           } while (++i < sizeof(av_buffer)-1  &&  av_buffer[i-1] != '\n');
757           av_buffer[i-1] = '\0';
758
759           /* Really search for virus again? */
760           if (malware_name == NULL)
761             /* try matcher on the line, grab substring */
762             malware_name = m_pcre_exec(fs_inf, av_buffer);
763         }
764         while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
765         break;
766       } /* fsecure */
767
768     case M_KAVD: /* "kavdaemon" scanner type -------------------------------- */
769       {
770         time_t t;
771         uschar tmpbuf[1024];
772         uschar * scanrequest;
773         int kav_rc;
774         unsigned long kav_reportlen, bread;
775         const pcre *kav_re;
776         uschar *p;
777
778         /* get current date and time, build scan request */
779         time(&t);
780         /* pdp note: before the eml_filename parameter, this scanned the
781         directory; not finding documentation, so we'll strip off the directory.
782         The side-effect is that the test framework scanning may end up in
783         scanning more than was requested, but for the normal interface, this is
784         fine. */
785
786         strftime(CS tmpbuf, sizeof(tmpbuf), "%d %b %H:%M:%S", localtime(&t));
787         scanrequest = string_sprintf("<0>%s:%s", CS tmpbuf, eml_filename);
788         p = Ustrrchr(scanrequest, '/');
789         if (p)
790           *p = '\0';
791
792         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
793             scanner_name, scanner_options);
794
795         /* send scan request */
796         if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0)
797           return m_errlog_defer(scanent, errstr);
798
799         /* wait for result */
800         if ((bread = recv(sock, tmpbuf, 2, 0) != 2))
801           return m_errlog_defer_3(scanent,
802                               US"unable to read 2 bytes from socket.", sock);
803
804         /* get errorcode from one nibble */
805         kav_rc = tmpbuf[ test_byte_order()==LITTLE_MY_ENDIAN ? 0 : 1 ] & 0x0F;
806         switch(kav_rc)
807         {
808         case 5: case 6: /* improper kavdaemon configuration */
809           return m_errlog_defer_3(scanent,
810                   US"please reconfigure kavdaemon to NOT disinfect or remove infected files.",
811                   sock);
812         case 1:
813           return m_errlog_defer_3(scanent,
814                   US"reported 'scanning not completed' (code 1).", sock);
815         case 7:
816           return m_errlog_defer_3(scanent,
817                   US"reported 'kavdaemon damaged' (code 7).", sock);
818         }
819
820         /* code 8 is not handled, since it is ambigous. It appears mostly on
821         bounces where part of a file has been cut off */
822
823         /* "virus found" return codes (2-4) */
824         if ((kav_rc > 1) && (kav_rc < 5)) {
825           int report_flag = 0;
826
827           /* setup default virus name */
828           malware_name = US"unknown";
829
830           report_flag = tmpbuf[ test_byte_order() == LITTLE_MY_ENDIAN ? 1 : 0 ];
831
832           /* read the report, if available */
833           if( report_flag == 1 ) {
834             /* read report size */
835             if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4)
836               return m_errlog_defer_3(scanent,
837                     US"cannot read report size", sock);
838
839             /* it's possible that avp returns av_buffer[1] == 1 but the
840             reportsize is 0 (!?) */
841             if (kav_reportlen > 0) {
842               /* set up match regex, depends on retcode */
843               kav_re = m_pcre_compile( kav_rc == 3
844                                        ? US"suspicion:\\s*(.+?)\\s*$"
845                                        : US"infected:\\s*(.+?)\\s*$",
846                                        &errstr );
847
848               /* read report, linewise */
849               while (kav_reportlen > 0) {
850                 bread = 0;
851                 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
852                   kav_reportlen--;
853                   if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
854                   bread++;
855                 }
856                 bread++;
857                 tmpbuf[bread] = '\0';
858
859                 /* try matcher on the line, grab substring */
860                 if ((malware_name = m_pcre_exec(kav_re, tmpbuf)))
861                   break;
862               }
863             }
864           }
865         }
866         else /* no virus found */
867           malware_name = NULL;
868
869         break;
870       }
871
872     case M_CMDL: /* "cmdline" scanner type ---------------------------------- */
873       {
874         const uschar *cmdline_scanner = scanner_options;
875         const pcre *cmdline_trigger_re;
876         const pcre *cmdline_regex_re;
877         uschar * file_name;
878         uschar * commandline;
879         void (*eximsigchld)(int);
880         void (*eximsigpipe)(int);
881         FILE *scanner_out = NULL;
882         FILE *scanner_record = NULL;
883         uschar linebuffer[32767];
884         int trigger = 0;
885         uschar *p;
886
887         if (!cmdline_scanner)
888           return m_errlog_defer(scanent, errstr);
889
890         /* find scanner output trigger */
891         cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep,
892                                   "missing trigger specification", &errstr);
893         if (!cmdline_trigger_re)
894           return m_errlog_defer(scanent, errstr);
895
896         /* find scanner name regex */
897         cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep,
898                             "missing virus name regex specification", &errstr);
899         if (!cmdline_regex_re)
900           return m_errlog_defer(scanent, errstr);
901
902         /* prepare scanner call; despite the naming, file_name holds a directory
903         name which is documented as the value given to %s. */
904
905         file_name = string_copy(eml_filename);
906         p = Ustrrchr(file_name, '/');
907         if (p)
908           *p = '\0';
909         commandline = string_sprintf(CS cmdline_scanner, file_name);
910
911         /* redirect STDERR too */
912         commandline = string_sprintf("%s 2>&1", commandline);
913
914         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
915
916         /* store exims signal handlers */
917         eximsigchld = signal(SIGCHLD,SIG_DFL);
918         eximsigpipe = signal(SIGPIPE,SIG_DFL);
919
920         if (!(scanner_out = popen(CS commandline,"r"))) {
921           int err = errno;
922           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
923           return m_errlog_defer(scanent,
924             string_sprintf("call (%s) failed: %s.", commandline, strerror(err)));
925         }
926
927         file_name = string_sprintf("%s/scan/%s/%s_scanner_output",
928                                   spool_directory, message_id, message_id);
929         scanner_record = modefopen(file_name, "wb", SPOOL_MODE);
930
931         if (scanner_record == NULL) {
932           int err = errno;
933           (void) pclose(scanner_out);
934           signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
935           return m_errlog_defer(scanent,
936             string_sprintf("opening scanner output file (%s) failed: %s.",
937               file_name, strerror(err)));
938         }
939
940         /* look for trigger while recording output */
941         while(fgets(CS linebuffer, sizeof(linebuffer), scanner_out)) {
942           if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
943             /* short write */
944             (void) pclose(scanner_out);
945             signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
946             return m_errlog_defer(scanent,
947               string_sprintf("short write on scanner output file (%s).", file_name));
948           }
949           /* try trigger match */
950           if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
951             trigger = 1;
952         }
953
954         (void)fclose(scanner_record);
955         sep = pclose(scanner_out);
956         signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe);
957         if (sep != 0)
958             return m_errlog_defer(scanent,
959                 sep == -1
960                 ? string_sprintf("running scanner failed: %s", strerror(sep))
961                 : string_sprintf("scanner returned error code: %d", sep));
962
963         if (trigger) {
964           uschar * s;
965           /* setup default virus name */
966           malware_name = US"unknown";
967
968           /* re-open the scanner output file, look for name match */
969           scanner_record = fopen(CS file_name, "rb");
970           while(fgets(CS linebuffer, sizeof(linebuffer), scanner_record)) {
971             /* try match */
972             if ((s = m_pcre_exec(cmdline_regex_re, linebuffer)))
973               malware_name = s;
974           }
975           (void)fclose(scanner_record);
976         }
977         else /* no virus found */
978           malware_name = NULL;
979         break;
980       } /* cmdline */
981
982     case M_SOPHIE: /* "sophie" scanner type --------------------------------- */
983       {
984         int bread = 0;
985         uschar *p;
986         uschar * file_name;
987         uschar av_buffer[1024];
988
989         /* pass the scan directory to sophie */
990         file_name = string_copy(eml_filename);
991         if ((p = Ustrrchr(file_name, '/')))
992           *p = '\0';
993
994         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
995             scanner_name, scanner_options);
996
997         if (  write(sock, file_name, Ustrlen(file_name)) < 0
998            || write(sock, "\n", 1) != 1
999            )
1000           return m_errlog_defer_3(scanent,
1001             string_sprintf("unable to write to UNIX socket (%s)", scanner_options),
1002             sock);
1003
1004         /* wait for result */
1005         memset(av_buffer, 0, sizeof(av_buffer));
1006         if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0))
1007           return m_errlog_defer_3(scanent,
1008             string_sprintf("unable to read from UNIX socket (%s)", scanner_options),
1009             sock);
1010
1011         /* infected ? */
1012         if (av_buffer[0] == '1') {
1013           uschar * s = Ustrchr(av_buffer, '\n');
1014           if (s)
1015             *s = '\0';
1016           malware_name = string_copy(&av_buffer[2]);
1017         }
1018         else if (!strncmp(CS av_buffer, "-1", 2))
1019           return m_errlog_defer_3(scanent, US"scanner reported error", sock);
1020         else /* all ok, no virus */
1021           malware_name = NULL;
1022
1023         break;
1024       }
1025
1026     case M_CLAMD: /* "clamd" scanner type ----------------------------------- */
1027       {
1028       /* This code was originally contributed by David Saez */
1029       /* There are three scanning methods available to us:
1030        *  (1) Use the SCAN command, pointing to a file in the filesystem
1031        *  (2) Use the STREAM command, send the data on a separate port
1032        *  (3) Use the zINSTREAM command, send the data inline
1033        * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1034        * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1035        * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1036        * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1037        * WITH_OLD_CLAMAV_STREAM is defined.
1038        * See Exim bug 926 for details.  */
1039
1040         uschar *p, *vname, *result_tag, *response_end;
1041         int bread=0;
1042         uschar * file_name;
1043         uschar av_buffer[1024];
1044         uschar *hostname = US"";
1045         host_item connhost;
1046         uschar *clamav_fbuf;
1047         int clam_fd, result;
1048         off_t fsize;
1049         unsigned int fsize_uint;
1050         BOOL use_scan_command = FALSE;
1051         clamd_address_container * clamd_address_vector[MAX_CLAMD_SERVERS];
1052         int current_server;
1053         int num_servers = 0;
1054   #ifdef WITH_OLD_CLAMAV_STREAM
1055         unsigned int port;
1056         uschar av_buffer2[1024];
1057         int sockData;
1058   #else
1059         uint32_t send_size, send_final_zeroblock;
1060   #endif
1061
1062         if (*scanner_options == '/')
1063           /* Local file; so we def want to use_scan_command and don't want to try
1064            * passing IP/port combinations */
1065           use_scan_command = TRUE;
1066         else {
1067           const uschar *address = scanner_options;
1068           uschar address_buffer[MAX_CLAMD_ADDRESS_LENGTH + 20];
1069
1070           /* Go through the rest of the list of host/port and construct an array
1071            * of servers to try. The first one is the bit we just passed from
1072            * scanner_options so process that first and then scan the remainder of
1073            * the address buffer */
1074           do {
1075             clamd_address_container *this_clamd;
1076
1077             /* The 'local' option means use the SCAN command over the network
1078              * socket (ie common file storage in use) */
1079             if (strcmpic(address,US"local") == 0) {
1080               use_scan_command = TRUE;
1081               continue;
1082             }
1083
1084             /* XXX: If unsuccessful we should free this memory */
1085             this_clamd =
1086                 (clamd_address_container *)store_get(sizeof(clamd_address_container));
1087
1088             /* extract host and port part */
1089             if( sscanf(CS address, "%" MAX_CLAMD_ADDRESS_LENGTH_S "s %u",
1090                    this_clamd->tcp_addr, &(this_clamd->tcp_port)) != 2 ) {
1091               (void) m_errlog_defer(scanent,
1092                           string_sprintf("invalid address '%s'", address));
1093               continue;
1094             }
1095
1096             clamd_address_vector[num_servers] = this_clamd;
1097             num_servers++;
1098             if (num_servers >= MAX_CLAMD_SERVERS) {
1099               (void) m_errlog_defer(scanent,
1100                     US"More than " MAX_CLAMD_SERVERS_S " clamd servers "
1101                     "specified; only using the first " MAX_CLAMD_SERVERS_S );
1102               break;
1103             }
1104           } while ((address = string_nextinlist(&av_scanner_work, &sep,
1105                                           address_buffer,
1106                                           sizeof(address_buffer))) != NULL);
1107
1108           /* check if we have at least one server */
1109           if (!num_servers)
1110             return m_errlog_defer(scanent,
1111               US"no useable server addresses in malware configuration option.");
1112         }
1113
1114         /* See the discussion of response formats below to see why we really don't
1115         like colons in filenames when passing filenames to ClamAV. */
1116         if (use_scan_command && Ustrchr(eml_filename, ':'))
1117           return m_errlog_defer(scanent,
1118             string_sprintf("local/SCAN mode incompatible with" \
1119               " : in path to email filename [%s]", eml_filename));
1120
1121         /* We have some network servers specified */
1122         if (num_servers) {
1123
1124           /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1125            * only supports AF_INET, but we should probably be looking to the
1126            * future and rewriting this to be protocol-independent anyway. */
1127
1128           while ( num_servers > 0 ) {
1129             /* Randomly pick a server to start with */
1130             current_server = random_number( num_servers );
1131
1132             debug_printf("trying server name %s, port %u\n",
1133                          clamd_address_vector[current_server]->tcp_addr,
1134                          clamd_address_vector[current_server]->tcp_port);
1135
1136             /* Lookup the host. This is to ensure that we connect to the same IP
1137              * on both connections (as one host could resolve to multiple ips) */
1138             sock= m_tcpsocket(clamd_address_vector[current_server]->tcp_addr,
1139                                 clamd_address_vector[current_server]->tcp_port,
1140                                 &connhost, &errstr);
1141             if (sock >= 0) {
1142               /* Connection successfully established with a server */
1143               hostname = clamd_address_vector[current_server]->tcp_addr;
1144               break;
1145             }
1146
1147             (void) m_errlog_defer(scanent, errstr);
1148
1149             /* Remove the server from the list. XXX We should free the memory */
1150             num_servers--;
1151             int i;
1152             for( i = current_server; i < num_servers; i++ )
1153               clamd_address_vector[i] = clamd_address_vector[i+1];
1154           }
1155
1156           if ( num_servers == 0 )
1157             return m_errlog_defer(scanent, US"all servers failed");
1158
1159         } else {
1160           if ((sock = m_unixsocket(scanner_options, &errstr)) < 0)
1161             return m_errlog_defer(scanent, errstr);
1162         }
1163
1164         /* have socket in variable "sock"; command to use is semi-independent of
1165          * the socket protocol.  We use SCAN if is local (either Unix/local
1166          * domain socket, or explicitly told local) else we stream the data.
1167          * How we stream the data depends upon how we were built.  */
1168
1169         if (!use_scan_command) {
1170
1171   #ifdef WITH_OLD_CLAMAV_STREAM
1172           /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1173            * that port on a second connection; then in the scan-method-neutral
1174            * part, read the response back on the original connection. */
1175
1176           DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1177               scanner_name);
1178
1179           /* Pass the string to ClamAV (7 = "STREAM\n") */
1180           if (m_sock_send(sock, US"STREAM\n", 7, &errstr) < 0)
1181             return m_errlog_defer(scanent, errstr);
1182
1183           memset(av_buffer2, 0, sizeof(av_buffer2));
1184           bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
1185
1186           if (bread < 0)
1187             return m_errlog_defer_3(scanent,
1188               string_sprintf("unable to read PORT from socket (%s)",
1189                   strerror(errno)),
1190               sock);
1191
1192           if (bread == sizeof(av_buffer2))
1193             return m_errlog_defer_3(scanent, "buffer too small", sock);
1194
1195           if (!(*av_buffer2))
1196             return m_errlog_defer_3(scanent, "ClamAV returned null", sock);
1197
1198           av_buffer2[bread] = '\0';
1199           if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 )
1200             return m_errlog_defer_3(scanent,
1201               string_sprintf("Expected port information from clamd, got '%s'",
1202                 av_buffer2),
1203               sock);
1204
1205           sockData = m_tcpsocket(connhost.address, port, NULL, &errstr);
1206           if (sockData < 0)
1207             return m_errlog_defer_3(scanent, errstr, sock);
1208
1209   #define CLOSE_SOCKDATA (void)close(sockData)
1210   #else /* WITH_OLD_CLAMAV_STREAM not defined */
1211           /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1212           chunks, <n> a 4-byte number (network order), terminated by a zero-length
1213           chunk. */
1214
1215           DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1216               scanner_name);
1217
1218           /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1219           if (send(sock, "zINSTREAM", 10, 0) < 0)
1220             return m_errlog_defer_3(scanent,
1221               string_sprintf("unable to send zINSTREAM to socket (%s)",
1222                 strerror(errno)),
1223               sock);
1224
1225   #define CLOSE_SOCKDATA /**/
1226   #endif
1227
1228           /* calc file size */
1229           if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) {
1230             int err = errno;
1231             CLOSE_SOCKDATA;
1232             return m_errlog_defer_3(scanent,
1233               string_sprintf("can't open spool file %s: %s",
1234                 eml_filename, strerror(err)),
1235               sock);
1236           }
1237           if ((fsize = lseek(clam_fd, 0, SEEK_END)) < 0) {
1238             int err = errno;
1239             CLOSE_SOCKDATA; (void)close(clam_fd);
1240             return m_errlog_defer_3(scanent,
1241               string_sprintf("can't seek spool file %s: %s",
1242                 eml_filename, strerror(err)),
1243               sock);
1244           }
1245           fsize_uint = (unsigned int) fsize;
1246           if ((off_t)fsize_uint != fsize) {
1247             CLOSE_SOCKDATA; (void)close(clam_fd);
1248             return m_errlog_defer_3(scanent,
1249               string_sprintf("seeking spool file %s, size overflow",
1250                 eml_filename),
1251               sock);
1252           }
1253           lseek(clam_fd, 0, SEEK_SET);
1254
1255           if (!(clamav_fbuf = (uschar *) malloc (fsize_uint))) {
1256             CLOSE_SOCKDATA; (void)close(clam_fd);
1257             return m_errlog_defer_3(scanent,
1258               string_sprintf("unable to allocate memory %u for file (%s)",
1259                 fsize_uint, eml_filename),
1260               sock);
1261           }
1262
1263           if ((result = read(clam_fd, clamav_fbuf, fsize_uint)) < 0) {
1264             int err = errno;
1265             free(clamav_fbuf); CLOSE_SOCKDATA; (void)close(clam_fd);
1266             return m_errlog_defer_3(scanent,
1267               string_sprintf("can't read spool file %s: %s",
1268                 eml_filename, strerror(err)),
1269               sock);
1270           }
1271           (void)close(clam_fd);
1272
1273           /* send file body to socket */
1274   #ifdef WITH_OLD_CLAMAV_STREAM
1275           if (send(sockData, clamav_fbuf, fsize_uint, 0) < 0) {
1276             free(clamav_fbuf); CLOSE_SOCKDATA;
1277             return m_errlog_defer_3(scanent,
1278               string_sprintf("unable to send file body to socket (%s:%u)",
1279                 hostname, port),
1280               sock);
1281           }
1282   #else
1283           send_size = htonl(fsize_uint);
1284           send_final_zeroblock = 0;
1285           if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1286               (send(sock, clamav_fbuf, fsize_uint, 0) < 0) ||
1287               (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1288             {
1289             free(clamav_fbuf);
1290             return m_errlog_defer_3(scanent,
1291               string_sprintf("unable to send file body to socket (%s)", hostname),
1292               sock);
1293             }
1294   #endif
1295
1296           free(clamav_fbuf);
1297
1298           CLOSE_SOCKDATA;
1299   #undef CLOSE_SOCKDATA
1300
1301         } else { /* use scan command */
1302           /* Send a SCAN command pointing to a filename; then in the then in the
1303            * scan-method-neutral part, read the response back */
1304
1305   /* ================================================================= */
1306
1307           /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1308           which dates to when ClamAV needed us to break apart the email into the
1309           MIME parts (eg, with the now deprecated demime condition coming first).
1310           Some time back, ClamAV gained the ability to deconstruct the emails, so
1311           doing this would actually have resulted in the mail attachments being
1312           scanned twice, in the broken out files and from the original .eml.
1313           Since ClamAV now handles emails (and has for quite some time) we can
1314           just use the email file itself. */
1315           /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1316           file_name = string_sprintf("SCAN %s\n", eml_filename);
1317
1318           DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1319               scanner_name, scanner_options);
1320
1321           if (send(sock, file_name, Ustrlen(file_name), 0) < 0)
1322             return m_errlog_defer_3(scanent,
1323               string_sprintf("unable to write to socket (%s)", strerror(errno)),
1324               sock);
1325
1326           /* Do not shut down the socket for writing; a user report noted that
1327            * clamd 0.70 does not react well to this. */
1328         }
1329         /* Commands have been sent, no matter which scan method or connection
1330          * type we're using; now just read the result, independent of method. */
1331
1332         /* Read the result */
1333         memset(av_buffer, 0, sizeof(av_buffer));
1334         bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
1335         (void)close(sock);
1336         sock = -1;
1337
1338         if (!(bread > 0))
1339           return m_errlog_defer(scanent,
1340             string_sprintf("unable to read from socket (%s)", strerror(errno)));
1341
1342         if (bread == sizeof(av_buffer))
1343           return m_errlog_defer(scanent, US"buffer too small");
1344         /* We're now assured of a NULL at the end of av_buffer */
1345
1346         /* Check the result. ClamAV returns one of two result formats.
1347         In the basic mode, the response is of the form:
1348           infected: -> "<filename>: <virusname> FOUND"
1349           not-infected: -> "<filename>: OK"
1350           error: -> "<filename>: <errcode> ERROR
1351         If the ExtendedDetectionInfo option has been turned on, then we get:
1352           "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1353         for the infected case.  Compare:
1354   /tmp/eicar.com: Eicar-Test-Signature FOUND
1355   /tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1356
1357         In the streaming case, clamd uses the filename "stream" which you should
1358         be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }.  (The
1359         client app will replace "stream" with the original filename before returning
1360         results to stdout, but the trace shows the data).
1361
1362         We will assume that the pathname passed to clamd from Exim does not contain
1363         a colon.  We will have whined loudly above if the eml_filename does (and we're
1364         passing a filename to clamd). */
1365
1366         if (!(*av_buffer))
1367           return m_errlog_defer(scanent, US"ClamAV returned null");
1368
1369         /* strip newline at the end (won't be present for zINSTREAM)
1370         (also any trailing whitespace, which shouldn't exist, but we depend upon
1371         this below, so double-check) */
1372         p = av_buffer + Ustrlen(av_buffer) - 1;
1373         if (*p == '\n') *p = '\0';
1374
1375         DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1376
1377         while (isspace(*--p) && (p > av_buffer))
1378           *p = '\0';
1379         if (*p) ++p;
1380         response_end = p;
1381
1382         /* colon in returned output? */
1383         if((p = Ustrchr(av_buffer,':')) == NULL)
1384           return m_errlog_defer(scanent,
1385             string_sprintf("ClamAV returned malformed result (missing colon): %s",
1386                     av_buffer));
1387
1388         /* strip filename */
1389         while (*p && isspace(*++p)) /**/;
1390         vname = p;
1391
1392         /* It would be bad to encounter a virus with "FOUND" in part of the name,
1393         but we should at least be resistant to it. */
1394         p = Ustrrchr(vname, ' ');
1395         result_tag = p ? p+1 : vname;
1396
1397         if (Ustrcmp(result_tag, "FOUND") == 0) {
1398           /* p should still be the whitespace before the result_tag */
1399           while (isspace(*p)) --p;
1400           *++p = '\0';
1401           /* Strip off the extended information too, which will be in parens
1402           after the virus name, with no intervening whitespace. */
1403           if (*--p == ')') {
1404             /* "(hash:size)", so previous '(' will do; if not found, we have
1405             a curious virus name, but not an error. */
1406             p = Ustrrchr(vname, '(');
1407             if (p)
1408               *p = '\0';
1409           }
1410           malware_name = string_copy(vname);
1411           DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1412
1413         } else if (Ustrcmp(result_tag, "ERROR") == 0)
1414           return m_errlog_defer(scanent,
1415             string_sprintf("ClamAV returned: %s", av_buffer));
1416
1417         else if (Ustrcmp(result_tag, "OK") == 0) {
1418           /* Everything should be OK */
1419           malware_name = NULL;
1420           DEBUG(D_acl) debug_printf("Malware not found\n");
1421
1422         } else
1423           return m_errlog_defer(scanent,
1424             string_sprintf("unparseable response from ClamAV: {%s}", av_buffer));
1425
1426         break;
1427       } /* clamd */
1428
1429     case M_SOCK: /* "sock" scanner type ------------------------------------- */
1430       /* This code was derived by Martin Poole from the clamd code contributed
1431          by David Saez and the cmdline code
1432       */
1433       {
1434         int bread;
1435         uschar * commandline;
1436         uschar av_buffer[1024];
1437         uschar * linebuffer;
1438         uschar * sockline_scanner;
1439         uschar sockline_scanner_default[] = "%s\n";
1440         const pcre *sockline_trig_re;
1441         const pcre *sockline_name_re;
1442
1443         /* find scanner command line */
1444         if ((sockline_scanner = string_nextinlist(&av_scanner_work, &sep,
1445                                             NULL, 0)))
1446         {       /* check for no expansions apart from one %s */
1447           uschar * s = Ustrchr(sockline_scanner, '%');
1448           if (s++)
1449             if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%'))
1450               return m_errlog_defer_3(scanent,
1451                                     US"unsafe sock scanner call spec", sock);
1452         }
1453         else
1454           sockline_scanner = sockline_scanner_default;
1455
1456         /* find scanner output trigger */
1457         sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1458                                   "missing trigger specification", &errstr);
1459         if (!sockline_trig_re)
1460           return m_errlog_defer_3(scanent, errstr, sock);
1461
1462         /* find virus name regex */
1463         sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep,
1464                             "missing virus name regex specification", &errstr);
1465         if (!sockline_name_re)
1466           return m_errlog_defer_3(scanent, errstr, sock);
1467
1468         /* prepare scanner call - security depends on expansions check above */
1469         commandline = string_sprintf("%s/scan/%s/%s.eml", spool_directory, message_id, message_id);
1470         commandline = string_sprintf( CS sockline_scanner, CS commandline);
1471
1472
1473         /* Pass the command string to the socket */
1474         if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0)
1475           return m_errlog_defer(scanent, errstr);
1476
1477         /* Read the result */
1478         memset(av_buffer, 0, sizeof(av_buffer));
1479         bread = read(sock, av_buffer, sizeof(av_buffer));
1480
1481         if (!(bread > 0))
1482           return m_errlog_defer_3(scanent,
1483             string_sprintf("unable to read from socket (%s)", strerror(errno)),
1484             sock);
1485
1486         if (bread == sizeof(av_buffer))
1487           return m_errlog_defer_3(scanent, US"buffer too small", sock);
1488         linebuffer = string_copy(av_buffer);
1489
1490         /* try trigger match */
1491         if (regex_match_and_setup(sockline_trig_re, linebuffer, 0, -1)) {
1492           if (!(malware_name = m_pcre_exec(sockline_name_re, av_buffer)))
1493             malware_name = US "unknown";
1494         }
1495         else /* no virus found */
1496           malware_name = NULL;
1497         break;
1498       }
1499
1500     case M_MKSD: /* "mksd" scanner type ------------------------------------- */
1501       {
1502         char *mksd_options_end;
1503         int mksd_maxproc = 1;  /* default, if no option supplied */
1504         int sock;
1505         int retval;
1506
1507         if (scanner_options) {
1508           mksd_maxproc = (int)strtol(CS scanner_options, &mksd_options_end, 10);
1509           if (  *scanner_options == '\0'
1510              || *mksd_options_end != '\0'
1511              || mksd_maxproc < 1
1512              || mksd_maxproc > 32
1513              )
1514             return m_errlog_defer(scanent,
1515               string_sprintf("invalid option '%s'", scanner_options));
1516         }
1517
1518         if((sock = m_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0)
1519           return m_errlog_defer(scanent, errstr);
1520
1521         malware_name = NULL;
1522
1523         DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1524
1525         if ((retval = mksd_scan_packed(scanent, sock, eml_filename)) != OK) {
1526           close (sock);
1527           return retval;
1528         }
1529         break;
1530       }
1531     case M_AVAST: /* "avast" scanner type ----------------------------------- */
1532       {
1533       int ovector[1*3];
1534       uschar buf[1024];
1535       uschar * scanrequest;
1536       const pcre * avast_clean_re, * avast_virus_re;
1537       enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage;
1538
1539       /* According to Martin Tuma @avast the protocol uses "escaped
1540       whitespace", that is, every embedded whitespace is backslash
1541       escaped, as well as backslash is protected by backslash.
1542       The returned lines contain the name of the scanned file, a tab
1543       and the [ ] marker.
1544       [+] - not infected
1545       [L] - infected
1546       [E] - some error occured
1547       Such marker follows the first non-escaped TAB.  */
1548       if (  !(avast_clean_re =
1549                 m_pcre_compile(US"(?!\\\\)\\t\\[\\+\\]", &errstr))
1550          || !(avast_virus_re =
1551                 m_pcre_compile(US"(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)",
1552                   &errstr))
1553          )
1554         return malware_errlog_defer(errstr);
1555
1556       /* wait for result */
1557       for (avast_stage = AVA_HELO; recv_line(sock, buf, sizeof(buf)) > 0; )
1558         {
1559         int slen = Ustrlen(buf);
1560         if (slen >= 1) 
1561           {
1562           DEBUG(D_acl) debug_printf("got from avast: %s\n", buf);
1563           switch (avast_stage)
1564             {
1565             case AVA_HELO:
1566               if (Ustrncmp(buf, "220", 3) != 0)
1567                 goto endloop;                   /* require a 220 */
1568               goto sendreq;
1569
1570             case AVA_OPT:
1571               if (Ustrncmp(buf, "210", 3) == 0)
1572                 break;                          /* ignore 210 responses */
1573               if (Ustrncmp(buf, "200", 3) != 0)
1574                 goto endloop;                   /* require a 200 */
1575
1576             sendreq:
1577               {
1578               int len;
1579               /* Check for another option to send. Newline-terminate it. */
1580               if ((scanrequest = string_nextinlist(&av_scanner_work, &sep,
1581                                 NULL, 0)))
1582                 {
1583                 scanrequest = string_sprintf("%s\n", scanrequest);
1584                 avast_stage = AVA_OPT;          /* just sent option */
1585                 }
1586               else
1587                 {
1588                 scanrequest = string_sprintf("SCAN %s/scan/%s\n",
1589                     spool_directory, message_id);
1590                 avast_stage = AVA_RSP;          /* just sent command */
1591                 }
1592
1593               /* send config-cmd or scan-request to socket */
1594               len = Ustrlen(scanrequest);
1595               if (send(sock, scanrequest, len, 0) < 0)
1596                 {
1597                 scanrequest[len-1] = '\0';
1598                 return m_errlog_defer_3(scanent, string_sprintf(
1599                       "unable to send request '%s' to socket (%s): %s",
1600                       scanrequest, scanner_options, strerror(errno)), sock);
1601                 }
1602               break;
1603               }
1604
1605             case AVA_RSP:
1606               if (Ustrncmp(buf, "210", 3) == 0)
1607                 break;  /* ignore the "210 SCAN DATA" message */
1608
1609               if (pcre_exec(avast_clean_re, NULL, CS buf, slen,
1610                     0, 0, ovector, nelements(ovector)) > 0)
1611                 break;
1612
1613               if ((malware_name = m_pcre_exec(avast_virus_re, buf)))
1614                 { /* remove backslash in front of [whitespace|backslash] */
1615                 uschar * p, * p0;
1616                 for (p = malware_name; *p; ++p) 
1617                   if (*p == '\\' && (isspace(p[1]) || p[1] == '\\'))
1618                     for (p0 = p; *p0; ++p0) *p0 = p0[1];
1619                 
1620                 avast_stage = AVA_DONE;
1621                 goto endloop;
1622                 }
1623
1624               if (Ustrncmp(buf, "200 SCAN OK", 11) == 0) 
1625                 { /* we're done finally */
1626                 if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */
1627                   return m_errlog_defer_3(scanent, string_sprintf(
1628                               "unable to send quit request to socket (%s): %s",
1629                               scanner_options, strerror(errno)),
1630                               sock);
1631                 malware_name = NULL;
1632                 avast_stage = AVA_DONE;
1633                 goto endloop;
1634                 }
1635
1636               /* here for any unexpected response from the scanner */
1637               goto endloop;
1638             }
1639         }
1640       }
1641       endloop:
1642
1643       switch(avast_stage)
1644         {
1645         case AVA_HELO:  
1646         case AVA_OPT:
1647         case AVA_RSP:   return m_errlog_defer_3(scanent, string_sprintf(
1648                           "invalid response from scanner: %s\n", buf), sock);
1649         default:        break;
1650         }
1651       }
1652     }   /* scanner type switch */
1653
1654     if (sock >= 0)
1655       (void) close (sock);
1656     malware_ok = TRUE;                  /* set "been here, done that" marker */
1657   }
1658
1659   /* match virus name against pattern (caseless ------->----------v) */
1660   if (malware_name && regex_match_and_setup(re, malware_name, 0, -1))
1661     {
1662     DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
1663     return OK;
1664     }
1665   else
1666     return FAIL;
1667 }
1668
1669
1670 /* simple wrapper for reading lines from sockets */
1671 int
1672 recv_line(int sock, uschar *buffer, int size)
1673 {
1674   uschar *p = buffer;
1675
1676   memset(buffer,0,size);
1677   /* read until \n */
1678   while(recv(sock,p,1,0) > -1) {
1679     if ((p-buffer) > (size-2)) break;
1680     if (*p == '\n') break;
1681     if (*p != '\r') p++;
1682   }
1683   *p = '\0';
1684
1685   return (p-buffer);
1686 }
1687
1688
1689 /* ============= private routines for the "mksd" scanner type ============== */
1690
1691 #include <sys/uio.h>
1692
1693 static inline int
1694 mksd_writev (int sock, struct iovec *iov, int iovcnt)
1695 {
1696   int i;
1697
1698   for (;;) {
1699     do
1700       i = writev (sock, iov, iovcnt);
1701     while ((i < 0) && (errno == EINTR));
1702     if (i <= 0) {
1703       (void) malware_errlog_defer(US"unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1704       return -1;
1705     }
1706
1707     for (;;)
1708       if (i >= iov->iov_len) {
1709         if (--iovcnt == 0)
1710           return 0;
1711         i -= iov->iov_len;
1712         iov++;
1713       } else {
1714         iov->iov_len -= i;
1715         iov->iov_base = CS iov->iov_base + i;
1716         break;
1717       }
1718   }
1719 }
1720
1721 static inline int
1722 mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
1723 {
1724   int offset = 0;
1725   int i;
1726
1727   do {
1728     if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1729       (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1730       return -1;
1731     }
1732
1733     offset += i;
1734     /* offset == av_buffer_size -> buffer full */
1735     if (offset == av_buffer_size) {
1736       (void) malware_errlog_defer(US"malformed reply received from mksd");
1737       return -1;
1738     }
1739   } while (av_buffer[offset-1] != '\n');
1740
1741   av_buffer[offset] = '\0';
1742   return offset;
1743 }
1744
1745 static inline int
1746 mksd_parse_line(struct scan * scanent, char *line)
1747 {
1748   char *p;
1749
1750   switch (*line) {
1751     case 'O': /* OK */
1752       return OK;
1753
1754     case 'E':
1755     case 'A': /* ERR */
1756       if ((p = strchr (line, '\n')) != NULL)
1757         *p = '\0';
1758       return m_errlog_defer(scanent,
1759         string_sprintf("scanner failed: %s", line));
1760
1761     default: /* VIR */
1762       if ((p = strchr (line, '\n')) != NULL) {
1763         *p = '\0';
1764         if (((p-line) > 5) && (line[3] == ' '))
1765           if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1766             *p = '\0';
1767             malware_name = string_copy(US line+4);
1768             return OK;
1769           }
1770       }
1771       return m_errlog_defer(scanent,
1772         string_sprintf("malformed reply received: %s", line));
1773   }
1774 }
1775
1776 static int
1777 mksd_scan_packed(struct scan * scanent, int sock, uschar *scan_filename)
1778 {
1779   struct iovec iov[3];
1780   const char *cmd = "MSQ\n";
1781   uschar av_buffer[1024];
1782
1783   iov[0].iov_base = (void *) cmd;
1784   iov[0].iov_len = 3;
1785   iov[1].iov_base = CS scan_filename;
1786   iov[1].iov_len = Ustrlen(scan_filename);
1787   iov[2].iov_base = (void *) (cmd + 3);
1788   iov[2].iov_len = 1;
1789
1790   if (mksd_writev (sock, iov, 3) < 0)
1791     return DEFER;
1792
1793   if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1794     return DEFER;
1795
1796   return mksd_parse_line (scanent, CS av_buffer);
1797 }
1798
1799 #endif /*WITH_CONTENT_SCAN*/
1800 /*
1801  * vi: aw ai sw=2
1802  */