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