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