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