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