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