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