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