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