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