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