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