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