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