ClamAV INSTREAM scanning by default, unless built with WITH_OLD_CLAMAV_STREAM.
[exim.git] / src / src / malware.c
CommitLineData
8544e77a 1/* $Cambridge: exim/src/src/malware.c,v 1.19 2010/06/05 11:13:30 pdp Exp $ */
8523533c
TK
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 */
8544e77a
PP
16static int mksd_scan_packed(int sock, uschar *scan_filename);
17static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking);
8523533c
TK
18
19/* SHUT_WR seems to be undefined on Unixware? */
20#ifndef SHUT_WR
21#define SHUT_WR 1
22#endif
23
a9ccd69a
PH
24
25#define MALWARE_TIMEOUT 120
26
27
8523533c
TK
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
2376f2f5
TK
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
8e669ac1 37/* Routine to check whether a system is big- or litte-endian.
8523533c
TK
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
42int test_byte_order(void);
43int 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
49uschar malware_name_buffer[256];
50int malware_ok = 0;
51
8544e77a
PP
52/* Gross hacks for the -bmalware option; perhaps we should just create
53the scan directory normally for that case, but look into rigging up the
54needed header variables if not already set on the command-line? */
55extern int spool_mbox_ok;
56extern 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
63filename; it's a wrapper around the malware_file function.
64
65Arguments:
66 listptr the list of options to the "malware = ..." ACL condition
67
68Returns: Exim message processing code (OK, FAIL, DEFER, ...)
69 where true means malware was found (condition applies)
70*/
8523533c 71int malware(uschar **listptr) {
8544e77a
PP
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
93normal processing. Scan any file, using the Exim scanning interface.
94This function tampers with various global variables so is unsafe to use
95in any other context.
96
97Arguments:
98 eml_filename a file holding the message to be scanned
99
100Returns: Exim message processing code (OK, FAIL, DEFER, ...)
101 where true means malware was found (condition applies)
102*/
103int malware_in_file(uschar *eml_filename) {
104 uschar *scan_options[2];
105 uschar message_id_buf[64];
106 int ret;
107
108 scan_options[0] = "*";
109 scan_options[1] = NULL;
110
111 /* spool_mbox() assumes various parameters exist, when creating
112 the relevant directory and the email within */
113 (void) string_format(message_id_buf, sizeof(message_id_buf),
114 US"dummy-%d", pseudo_random_number(INT_MAX));
115 message_id = message_id_buf;
116 sender_address = "malware-sender@example.net";
117 return_path = "";
118 recipients_list = NULL;
119 receive_add_recipient("malware-victim@example.net", -1);
120 enable_dollar_recipients = TRUE;
121
122 ret = malware_internal(scan_options, eml_filename, TRUE);
123
124 strncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
125 spool_mbox_ok = 1;
126 /* don't set no_mbox_unspool; at present, there's no way for it to become
127 set, but if that changes, then it should apply to these tests too */
128 unspool_mbox();
129
130 return ret;
131}
132
133
134/*************************************************
135* Scan content for malware *
136*************************************************/
137
138/* This is an internal interface for scanning an email; the normal interface
139is via malware(), or there's malware_in_file() used for testing/debugging.
140
141Arguments:
142 listptr the list of options to the "malware = ..." ACL condition
143 eml_filename the file holding the email to be scanned
144 faking whether or not we're faking this up for the -bmalware test
145
146Returns: Exim message processing code (OK, FAIL, DEFER, ...)
147 where true means malware was found (condition applies)
148*/
149static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
8523533c
TK
150 int sep = 0;
151 uschar *list = *listptr;
152 uschar *av_scanner_work = av_scanner;
153 uschar *scanner_name;
154 uschar scanner_name_buffer[16];
155 uschar *malware_regex;
156 uschar malware_regex_buffer[64];
157 uschar malware_regex_default[] = ".+";
f7b63901 158 unsigned long mbox_size;
8523533c
TK
159 FILE *mbox_file;
160 int roffset;
161 const pcre *re;
162 const uschar *rerror;
8e669ac1 163
8523533c 164 /* make sure the eml mbox file is spooled up */
8544e77a 165 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
8523533c
TK
166 if (mbox_file == NULL) {
167 /* error while spooling */
168 log_write(0, LOG_MAIN|LOG_PANIC,
169 "malware acl condition: error while creating mbox spool file");
8e669ac1 170 return DEFER;
8523533c
TK
171 };
172 /* none of our current scanners need the mbox
173 file as a stream, so we can close it right away */
f1e894f3 174 (void)fclose(mbox_file);
8e669ac1 175
8523533c
TK
176 /* extract the malware regex to match against from the option list */
177 if ((malware_regex = string_nextinlist(&list, &sep,
178 malware_regex_buffer,
179 sizeof(malware_regex_buffer))) != NULL) {
8e669ac1 180
8523533c 181 /* parse 1st option */
8e669ac1 182 if ( (strcmpic(malware_regex,US"false") == 0) ||
8523533c
TK
183 (Ustrcmp(malware_regex,"0") == 0) ) {
184 /* explicitly no matching */
185 return FAIL;
186 };
8e669ac1 187
8523533c 188 /* special cases (match anything except empty) */
8e669ac1
PH
189 if ( (strcmpic(malware_regex,US"true") == 0) ||
190 (Ustrcmp(malware_regex,"*") == 0) ||
8523533c
TK
191 (Ustrcmp(malware_regex,"1") == 0) ) {
192 malware_regex = malware_regex_default;
193 };
194 }
195 else {
196 /* empty means "don't match anything" */
197 return FAIL;
198 };
199
ee873c8c
TK
200 /* Reset sep that is set by previous string_nextinlist() call */
201 sep = 0;
202
8523533c
TK
203 /* compile the regex, see if it works */
204 re = pcre_compile(CS malware_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
205 if (re == NULL) {
206 log_write(0, LOG_MAIN|LOG_PANIC,
207 "malware acl condition: regular expression error in '%s': %s at offset %d", malware_regex, rerror, roffset);
208 return DEFER;
209 };
210
211 /* if av_scanner starts with a dollar, expand it first */
212 if (*av_scanner == '$') {
213 av_scanner_work = expand_string(av_scanner);
214 if (av_scanner_work == NULL) {
215 log_write(0, LOG_MAIN|LOG_PANIC,
216 "malware acl condition: av_scanner starts with $, but expansion failed: %s", expand_string_message);
217 return DEFER;
218 }
219 else {
220 debug_printf("Expanded av_scanner global: %s\n", av_scanner_work);
221 /* disable result caching in this case */
222 malware_name = NULL;
223 malware_ok = 0;
224 };
225 }
226
227 /* Do not scan twice. */
228 if (malware_ok == 0) {
229
230 /* find the scanner type from the av_scanner option */
231 if ((scanner_name = string_nextinlist(&av_scanner_work, &sep,
232 scanner_name_buffer,
233 sizeof(scanner_name_buffer))) == NULL) {
234 /* no scanner given */
235 log_write(0, LOG_MAIN|LOG_PANIC,
236 "malware acl condition: av_scanner configuration variable is empty");
237 return DEFER;
238 };
8e669ac1 239
e5496194
TK
240 /* "f-protd" scanner type ----------------------------------------------- */
241 if (strcmpic(scanner_name, US"f-protd") == 0) {
242 uschar *fp_options, *fp_scan_option;
243 uschar fp_scan_option_buffer[1024];
244 uschar fp_options_buffer[1024];
245 uschar fp_options_default[] = "localhost 10200-10204";
246 uschar hostname[256];
247 unsigned int port, portlow, porthigh, connect_ok=0, detected=0, par_count = 0;
248 struct hostent *he;
249 struct in_addr in;
250 int sock;
251 uschar scanrequest[2048], buf[32768], *strhelper, *strhelper2;
252
253 if ((fp_options = string_nextinlist(&av_scanner_work, &sep,
254 fp_options_buffer, sizeof(fp_options_buffer))) == NULL) {
255 /* no options supplied, use default options */
256 fp_options = fp_options_default;
257 };
258
259 /* extract host and port part */
260 if ( sscanf(CS fp_options, "%s %u-%u", hostname, &portlow, &porthigh) != 3 ) {
261 if ( sscanf(CS fp_options, "%s %u", hostname, &portlow) != 2 ) {
262 log_write(0, LOG_MAIN|LOG_PANIC,
263 "malware acl condition: f-protd: invalid socket '%s'", fp_options);
264 return DEFER;
265 }
266 porthigh = portlow;
267 }
268
269 /* Lookup the host */
270 if((he = gethostbyname(CS hostname)) == 0) {
271 log_write(0, LOG_MAIN|LOG_PANIC,
272 "malware acl condition: f-protd: failed to lookup host '%s'", hostname);
273 return DEFER;
274 }
275
276 in = *(struct in_addr *) he->h_addr_list[0];
277 port = portlow;
278
279
280 /* Open the f-protd TCP socket */
281 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
282 log_write(0, LOG_MAIN|LOG_PANIC,
283 "malware acl condition: f-protd: unable to acquire socket (%s)",
284 strerror(errno));
285 return DEFER;
286 }
287
288 /* Try to connect to all portslow-high until connection is established */
289 for (port = portlow; !connect_ok && port < porthigh; port++) {
290 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) >= 0) {
291 connect_ok = 1;
292 }
293 }
294
295 if ( !connect_ok ) {
296 log_write(0, LOG_MAIN|LOG_PANIC,
297 "malware acl condition: f-protd: connection to %s, port %u-%u failed (%s)",
298 inet_ntoa(in), portlow, porthigh, strerror(errno));
299 (void)close(sock);
300 return DEFER;
301 }
302
8544e77a
PP
303 DEBUG(D_acl) debug_printf("Malware scan: issuing %s GET\n", scanner_name);
304 (void)string_format(scanrequest, 1024, CS"GET %s", eml_filename);
e5496194
TK
305
306 while ((fp_scan_option = string_nextinlist(&av_scanner_work, &sep,
307 fp_scan_option_buffer, sizeof(fp_scan_option_buffer))) != NULL) {
308 if ( par_count ) {
309 Ustrcat(scanrequest, "%20");
310 } else {
311 Ustrcat(scanrequest, "?");
312 }
313 Ustrcat(scanrequest, fp_scan_option);
314 par_count++;
315 }
316 Ustrcat(scanrequest, " HTTP/1.0\r\n\r\n");
317
318 /* send scan request */
319 if (send(sock, &scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
320 (void)close(sock);
321 log_write(0, LOG_MAIN|LOG_PANIC,
322 "malware acl condition: f-protd: unable to send command to socket (%s)", scanrequest);
323 return DEFER;
324 }
325
326 /* We get a lot of empty lines, so we need this hack to check for any data at all */
327 while( recv(sock, buf, 1, MSG_PEEK) > 0 ) {
328 if ( recv_line(sock, buf, 32768) > 0) {
329 if ( Ustrstr(buf, US"<detected type=\"") != NULL ) {
330 detected = 1;
331 } else if ( detected && (strhelper = Ustrstr(buf, US"<name>")) ) {
7156b1ef 332 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
e5496194
TK
333 *strhelper2 = '\0';
334 Ustrcpy(malware_name_buffer, strhelper + 6);
335 }
336 } else if ( Ustrstr(buf, US"<summary code=\"") ) {
337 if ( Ustrstr(buf, US"<summary code=\"11\">") ) {
338 malware_name = malware_name_buffer;
339 } else {
340 malware_name = NULL;
341 }
342 }
343 }
344 }
345 (void)close(sock);
346 }
384152a6
TK
347 /* "drweb" scanner type ----------------------------------------------- */
348 /* v0.1 - added support for tcp sockets */
349 /* v0.0 - initial release -- support for unix sockets */
e5496194 350 else if (strcmpic(scanner_name,US"drweb") == 0) {
384152a6
TK
351 uschar *drweb_options;
352 uschar drweb_options_buffer[1024];
353 uschar drweb_options_default[] = "/usr/local/drweb/run/drwebd.sock";
354 struct sockaddr_un server;
355 int sock, result, ovector[30];
356 unsigned int port, fsize;
357 uschar tmpbuf[1024], *drweb_fbuf;
384152a6
TK
358 uschar drweb_match_string[128];
359 int drweb_rc, drweb_cmd, drweb_flags = 0x0000, drweb_fd,
360 drweb_vnum, drweb_slen, drweb_fin = 0x0000;
361 unsigned long bread;
362 uschar hostname[256];
363 struct hostent *he;
364 struct in_addr in;
365 pcre *drweb_re;
366
367 if ((drweb_options = string_nextinlist(&av_scanner_work, &sep,
368 drweb_options_buffer, sizeof(drweb_options_buffer))) == NULL) {
369 /* no options supplied, use default options */
370 drweb_options = drweb_options_default;
371 };
372
373 if (*drweb_options != '/') {
374
375 /* extract host and port part */
376 if( sscanf(CS drweb_options, "%s %u", hostname, &port) != 2 ) {
377 log_write(0, LOG_MAIN|LOG_PANIC,
378 "malware acl condition: drweb: invalid socket '%s'", drweb_options);
379 return DEFER;
380 }
381
382 /* Lookup the host */
383 if((he = gethostbyname(CS hostname)) == 0) {
384 log_write(0, LOG_MAIN|LOG_PANIC,
385 "malware acl condition: drweb: failed to lookup host '%s'", hostname);
386 return DEFER;
387 }
388
389 in = *(struct in_addr *) he->h_addr_list[0];
390
391 /* Open the drwebd TCP socket */
392 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
393 log_write(0, LOG_MAIN|LOG_PANIC,
394 "malware acl condition: drweb: unable to acquire socket (%s)",
395 strerror(errno));
396 return DEFER;
397 }
398
399 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
f1e894f3 400 (void)close(sock);
384152a6
TK
401 log_write(0, LOG_MAIN|LOG_PANIC,
402 "malware acl condition: drweb: connection to %s, port %u failed (%s)",
403 inet_ntoa(in), port, strerror(errno));
404 return DEFER;
405 }
406
407 /* prepare variables */
408 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
409 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
384152a6
TK
410
411 /* calc file size */
8544e77a 412 drweb_fd = open(CS eml_filename, O_RDONLY);
384152a6 413 if (drweb_fd == -1) {
f1e894f3 414 (void)close(sock);
384152a6
TK
415 log_write(0, LOG_MAIN|LOG_PANIC,
416 "malware acl condition: drweb: can't open spool file %s: %s",
8544e77a 417 eml_filename, strerror(errno));
384152a6
TK
418 return DEFER;
419 }
420 fsize = lseek(drweb_fd, 0, SEEK_END);
421 if (fsize == -1) {
f1e894f3
PH
422 (void)close(sock);
423 (void)close(drweb_fd);
384152a6
TK
424 log_write(0, LOG_MAIN|LOG_PANIC,
425 "malware acl condition: drweb: can't seek spool file %s: %s",
8544e77a 426 eml_filename, strerror(errno));
384152a6
TK
427 return DEFER;
428 }
429 drweb_slen = htonl(fsize);
430 lseek(drweb_fd, 0, SEEK_SET);
431
8544e77a
PP
432 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
433 scanner_name, hostname, port);
434
384152a6
TK
435 /* send scan request */
436 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
437 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
438 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0) ||
439 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) {
f1e894f3
PH
440 (void)close(sock);
441 (void)close(drweb_fd);
384152a6
TK
442 log_write(0, LOG_MAIN|LOG_PANIC,
443 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
444 return DEFER;
445 }
446
447 drweb_fbuf = (uschar *) malloc (fsize);
448 if (!drweb_fbuf) {
f1e894f3
PH
449 (void)close(sock);
450 (void)close(drweb_fd);
384152a6
TK
451 log_write(0, LOG_MAIN|LOG_PANIC,
452 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
8544e77a 453 fsize, eml_filename);
384152a6
TK
454 return DEFER;
455 }
456
457 result = read (drweb_fd, drweb_fbuf, fsize);
458 if (result == -1) {
f1e894f3
PH
459 (void)close(sock);
460 (void)close(drweb_fd);
384152a6
TK
461 free(drweb_fbuf);
462 log_write(0, LOG_MAIN|LOG_PANIC,
463 "malware acl condition: drweb: can't read spool file %s: %s",
8544e77a 464 eml_filename, strerror(errno));
384152a6
TK
465 return DEFER;
466 }
f1e894f3 467 (void)close(drweb_fd);
384152a6
TK
468
469 /* send file body to socket */
470 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
f1e894f3 471 (void)close(sock);
384152a6
TK
472 free(drweb_fbuf);
473 log_write(0, LOG_MAIN|LOG_PANIC,
474 "malware acl condition: drweb: unable to send file body to socket (%s)", drweb_options);
475 return DEFER;
476 }
f1e894f3 477 (void)close(drweb_fd);
384152a6
TK
478 }
479 else {
480 /* open the drwebd UNIX socket */
481 sock = socket(AF_UNIX, SOCK_STREAM, 0);
482 if (sock < 0) {
483 log_write(0, LOG_MAIN|LOG_PANIC,
484 "malware acl condition: drweb: can't open UNIX socket");
485 return DEFER;
486 }
487 server.sun_family = AF_UNIX;
488 Ustrcpy(server.sun_path, drweb_options);
489 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
f1e894f3 490 (void)close(sock);
384152a6
TK
491 log_write(0, LOG_MAIN|LOG_PANIC,
492 "malware acl condition: drweb: unable to connect to socket (%s). errno=%d", drweb_options, errno);
493 return DEFER;
494 }
495
496 /* prepare variables */
497 drweb_cmd = htonl(DRWEBD_SCAN_CMD);
498 drweb_flags = htonl(DRWEBD_RETURN_VIRUSES | DRWEBD_IS_MAIL);
8544e77a
PP
499 drweb_slen = htonl(Ustrlen(eml_filename));
500
501 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local scan [%s]\n",
502 scanner_name, drweb_options);
384152a6
TK
503
504 /* send scan request */
505 if ((send(sock, &drweb_cmd, sizeof(drweb_cmd), 0) < 0) ||
506 (send(sock, &drweb_flags, sizeof(drweb_flags), 0) < 0) ||
507 (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) ||
8544e77a 508 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
384152a6 509 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
f1e894f3 510 (void)close(sock);
384152a6
TK
511 log_write(0, LOG_MAIN|LOG_PANIC,
512 "malware acl condition: drweb: unable to send commands to socket (%s)", drweb_options);
513 return DEFER;
514 }
515 }
516
517 /* wait for result */
518 if ((bread = recv(sock, &drweb_rc, sizeof(drweb_rc), 0) != sizeof(drweb_rc))) {
f1e894f3 519 (void)close(sock);
384152a6
TK
520 log_write(0, LOG_MAIN|LOG_PANIC,
521 "malware acl condition: drweb: unable to read return code");
522 return DEFER;
523 }
524 drweb_rc = ntohl(drweb_rc);
525
526 if ((bread = recv(sock, &drweb_vnum, sizeof(drweb_vnum), 0) != sizeof(drweb_vnum))) {
f1e894f3 527 (void)close(sock);
384152a6
TK
528 log_write(0, LOG_MAIN|LOG_PANIC,
529 "malware acl condition: drweb: unable to read the number of viruses");
530 return DEFER;
531 }
532 drweb_vnum = ntohl(drweb_vnum);
533
534 /* "virus(es) found" if virus number is > 0 */
535 if (drweb_vnum)
536 {
537 int i;
538 uschar pre_malware_nb[256];
539
540 malware_name = malware_name_buffer;
541
542 /* setup default virus name */
543 Ustrcpy(malware_name_buffer,"unknown");
544
545 /* read and concatenate virus names into one string */
546 for (i=0;i<drweb_vnum;i++)
547 {
548 /* read the size of report */
549 if ((bread = recv(sock, &drweb_slen, sizeof(drweb_slen), 0) != sizeof(drweb_slen))) {
f1e894f3 550 (void)close(sock);
384152a6
TK
551 log_write(0, LOG_MAIN|LOG_PANIC,
552 "malware acl condition: drweb: cannot read report size");
553 return DEFER;
554 };
555 drweb_slen = ntohl(drweb_slen);
556
557 /* read report body */
558 if ((bread = recv(sock, tmpbuf, drweb_slen, 0)) != drweb_slen) {
f1e894f3 559 (void)close(sock);
384152a6
TK
560 log_write(0, LOG_MAIN|LOG_PANIC,
561 "malware acl condition: drweb: cannot read report string");
562 return DEFER;
563 };
564 tmpbuf[drweb_slen] = '\0';
565
566 /* set up match regex, depends on retcode */
567 Ustrcpy(drweb_match_string, "infected\\swith\\s*(.+?)$");
568
569 drweb_re = pcre_compile( CS drweb_match_string,
570 PCRE_COPT,
571 (const char **)&rerror,
572 &roffset,
573 NULL );
574
575 /* try matcher on the line, grab substring */
576 result = pcre_exec(drweb_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
577 if (result >= 2) {
578 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS pre_malware_nb, 255);
579 }
580 /* the first name we just copy to malware_name */
581 if (i==0)
582 Ustrcpy(CS malware_name_buffer, CS pre_malware_nb);
583 else {
584 /* concatenate each new virus name to previous */
585 int slen = Ustrlen(malware_name_buffer);
586 if (slen < (slen+Ustrlen(pre_malware_nb))) {
587 Ustrcat(malware_name_buffer, "/");
588 Ustrcat(malware_name_buffer, pre_malware_nb);
589 }
590 }
591 }
592 }
593 else {
594 char *drweb_s = NULL;
595
596 if (drweb_rc & DERR_READ_ERR) drweb_s = "read error";
597 if (drweb_rc & DERR_NOMEMORY) drweb_s = "no memory";
598 if (drweb_rc & DERR_TIMEOUT) drweb_s = "timeout";
599 if (drweb_rc & DERR_BAD_CALL) drweb_s = "wrong command";
600 /* retcodes DERR_SYMLINK, DERR_NO_REGFILE, DERR_SKIPPED.
601 * DERR_TOO_BIG, DERR_TOO_COMPRESSED, DERR_SPAM,
602 * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR
603 * and others are ignored */
604 if (drweb_s) {
605 log_write(0, LOG_MAIN|LOG_PANIC,
606 "malware acl condition: drweb: drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s);
f1e894f3 607 (void)close(sock);
384152a6
TK
608 return DEFER;
609 }
610 /* no virus found */
611 malware_name = NULL;
612 };
f1e894f3 613 (void)close(sock);
384152a6
TK
614 }
615 /* ----------------------------------------------------------------------- */
8523533c
TK
616 else if (strcmpic(scanner_name,US"aveserver") == 0) {
617 uschar *kav_options;
618 uschar kav_options_buffer[1024];
619 uschar kav_options_default[] = "/var/run/aveserver";
620 uschar buf[32768];
8523533c
TK
621 struct sockaddr_un server;
622 int sock;
7e274b4b 623 int result;
8e669ac1 624
8523533c
TK
625 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
626 kav_options_buffer,
627 sizeof(kav_options_buffer))) == NULL) {
628 /* no options supplied, use default options */
629 kav_options = kav_options_default;
630 };
8e669ac1 631
8523533c
TK
632 /* open the aveserver socket */
633 sock = socket(AF_UNIX, SOCK_STREAM, 0);
634 if (sock < 0) {
635 log_write(0, LOG_MAIN|LOG_PANIC,
636 "malware acl condition: can't open UNIX socket.");
8e669ac1 637 return DEFER;
8523533c
TK
638 }
639 server.sun_family = AF_UNIX;
640 Ustrcpy(server.sun_path, kav_options);
641 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
f1e894f3 642 (void)close(sock);
8523533c
TK
643 log_write(0, LOG_MAIN|LOG_PANIC,
644 "malware acl condition: unable to connect to aveserver UNIX socket (%s). errno=%d", kav_options, errno);
645 return DEFER;
646 }
8e669ac1 647
8523533c
TK
648 /* read aveserver's greeting and see if it is ready (2xx greeting) */
649 recv_line(sock, buf, 32768);
650
651 if (buf[0] != '2') {
652 /* aveserver is having problems */
f1e894f3 653 (void)close(sock);
8523533c
TK
654 log_write(0, LOG_MAIN|LOG_PANIC,
655 "malware acl condition: aveserver is unavailable (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
656 return DEFER;
657 };
8e669ac1 658
8523533c 659 /* prepare our command */
8544e77a
PP
660 (void)string_format(buf, 32768, "SCAN bPQRSTUW %s\r\n", eml_filename);
661
662 DEBUG(D_acl) debug_printf("Malware scan: issuing %s SCAN\n", scanner_name);
8e669ac1 663
8523533c
TK
664 /* and send it */
665 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
f1e894f3 666 (void)close(sock);
8523533c
TK
667 log_write(0, LOG_MAIN|LOG_PANIC,
668 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
669 return DEFER;
670 }
8e669ac1 671
8523533c 672 malware_name = NULL;
7e274b4b 673 result = 0;
8523533c
TK
674 /* read response lines, find malware name and final response */
675 while (recv_line(sock, buf, 32768) > 0) {
676 debug_printf("aveserver: %s\n", buf);
7e274b4b
TK
677 if (buf[0] == '2') {
678 break;
679 } else if (buf[0] == '5') {
680 /* aveserver is having problems */
681 log_write(0, LOG_MAIN|LOG_PANIC,
8544e77a
PP
682 "malware acl condition: unable to scan file %s (Responded: %s).",
683 eml_filename, buf);
7e274b4b
TK
684 result = DEFER;
685 break;
686 } else if (Ustrncmp(buf,"322",3) == 0) {
8523533c
TK
687 uschar *p = Ustrchr(&buf[4],' ');
688 *p = '\0';
689 Ustrcpy(malware_name_buffer,&buf[4]);
690 malware_name = malware_name_buffer;
7e274b4b
TK
691 };
692 }
693
694 /* prepare our command */
b07e6aa3 695 (void)string_format(buf, 32768, "quit\r\n");
7e274b4b
TK
696
697 /* and send it */
698 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
f1e894f3 699 (void)close(sock);
7e274b4b
TK
700 log_write(0, LOG_MAIN|LOG_PANIC,
701 "malware acl condition: unable to write to aveserver UNIX socket (%s)", kav_options);
702 return DEFER;
8523533c
TK
703 }
704
7e274b4b
TK
705 /* read aveserver's greeting and see if it is ready (2xx greeting) */
706 recv_line(sock, buf, 32768);
707
708 if (buf[0] != '2') {
709 /* aveserver is having problems */
f1e894f3 710 (void)close(sock);
7e274b4b
TK
711 log_write(0, LOG_MAIN|LOG_PANIC,
712 "malware acl condition: unable to quit aveserver dialogue (Responded: %s).", ((buf[0] != 0) ? buf : (uschar *)"nothing") );
713 return DEFER;
714 };
715
f1e894f3 716 (void)close(sock);
7e274b4b
TK
717
718 if (result == DEFER) return DEFER;
8523533c
TK
719 }
720 /* "fsecure" scanner type ------------------------------------------------- */
721 else if (strcmpic(scanner_name,US"fsecure") == 0) {
722 uschar *fsecure_options;
723 uschar fsecure_options_buffer[1024];
724 uschar fsecure_options_default[] = "/var/run/.fsav";
725 struct sockaddr_un server;
726 int sock, i, j, bread = 0;
727 uschar file_name[1024];
728 uschar av_buffer[1024];
729 pcre *fs_inf;
f7b63901
PH
730 static uschar *cmdoptions[] = { US"CONFIGURE\tARCHIVE\t1\n",
731 US"CONFIGURE\tTIMEOUT\t0\n",
732 US"CONFIGURE\tMAXARCH\t5\n",
733 US"CONFIGURE\tMIME\t1\n" };
8e669ac1 734
8523533c
TK
735 malware_name = NULL;
736 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
737 fsecure_options_buffer,
8e669ac1 738 sizeof(fsecure_options_buffer))) == NULL) {
8523533c
TK
739 /* no options supplied, use default options */
740 fsecure_options = fsecure_options_default;
741 };
8e669ac1 742
8523533c
TK
743 /* open the fsecure socket */
744 sock = socket(AF_UNIX, SOCK_STREAM, 0);
745 if (sock < 0) {
746 log_write(0, LOG_MAIN|LOG_PANIC,
747 "malware acl condition: unable to open fsecure socket %s (%s)",
748 fsecure_options, strerror(errno));
749 return DEFER;
750 }
751 server.sun_family = AF_UNIX;
752 Ustrcpy(server.sun_path, fsecure_options);
753 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
f1e894f3 754 (void)close(sock);
8523533c
TK
755 log_write(0, LOG_MAIN|LOG_PANIC,
756 "malware acl condition: unable to connect to fsecure socket %s (%s)",
757 fsecure_options, strerror(errno));
758 return DEFER;
759 }
8e669ac1 760
8544e77a
PP
761 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
762 scanner_name, fsecure_options);
763
8523533c
TK
764 /* pass options */
765 memset(av_buffer, 0, sizeof(av_buffer));
766 for (i=0; i != 4; i++) {
767 /* debug_printf("send option \"%s\"",cmdoptions[i]); */
768 if (write(sock, cmdoptions[i], Ustrlen(cmdoptions[i])) < 0) {
f1e894f3 769 (void)close(sock);
8523533c
TK
770 log_write(0, LOG_MAIN|LOG_PANIC,
771 "malware acl condition: unable to write fsecure option %d to %s (%s)",
772 i, fsecure_options, strerror(errno));
8e669ac1 773 return DEFER;
8523533c 774 };
8e669ac1 775
a9ccd69a 776 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
8523533c
TK
777 if (bread >0) av_buffer[bread]='\0';
778 if (bread < 0) {
f1e894f3 779 (void)close(sock);
8523533c
TK
780 log_write(0, LOG_MAIN|LOG_PANIC,
781 "malware acl condition: unable to read fsecure answer %d (%s)", i, strerror(errno));
782 return DEFER;
783 };
784 for (j=0;j<bread;j++) if((av_buffer[j]=='\r')||(av_buffer[j]=='\n')) av_buffer[j] ='@';
785 /* debug_printf("read answer %d read=%d \"%s\"\n", i, bread, av_buffer ); */
786 /* while (Ustrstr(av_buffer, "OK\tServer configured.@") == NULL); */
787 };
8e669ac1 788
8523533c 789 /* pass the mailfile to fsecure */
8544e77a 790 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
8523533c
TK
791 /* debug_printf("send scan %s",file_name); */
792 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
f1e894f3 793 (void)close(sock);
8523533c
TK
794 log_write(0, LOG_MAIN|LOG_PANIC,
795 "malware acl condition: unable to write fsecure scan to %s (%s)",
796 fsecure_options, strerror(errno));
797 return DEFER;
798 };
8e669ac1 799
8523533c
TK
800 /* set up match */
801 /* todo also SUSPICION\t */
802 fs_inf = pcre_compile("\\S{0,5}INFECTED\\t[^\\t]*\\t([^\\t]+)\\t\\S*$", PCRE_COPT, (const char **)&rerror, &roffset, NULL);
8e669ac1 803
8523533c
TK
804 /* read report, linewise */
805 do {
806 int ovector[30];
807 i = 0;
808 memset(av_buffer, 0, sizeof(av_buffer));
809 do {
a9ccd69a 810 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
8523533c 811 if (bread < 0) {
f1e894f3 812 (void)close(sock);
8523533c
TK
813 log_write(0, LOG_MAIN|LOG_PANIC,
814 "malware acl condition: unable to read fsecure result (%s)", strerror(errno));
815 return DEFER;
816 };
817 i++;
818 }
819 while ((i < sizeof(av_buffer)-1 ) && (av_buffer[i-1] != '\n'));
820 av_buffer[i-1] = '\0';
821 /* debug_printf("got line \"%s\"\n",av_buffer); */
8e669ac1 822
8523533c
TK
823 /* Really search for virus again? */
824 if (malware_name == NULL) {
825 /* try matcher on the line, grab substring */
826 i = pcre_exec(fs_inf, NULL, CS av_buffer, Ustrlen(av_buffer), 0, 0, ovector, 30);
827 if (i >= 2) {
828 /* Got it */
829 pcre_copy_substring(CS av_buffer, ovector, i, 1, CS malware_name_buffer, 255);
830 malware_name = malware_name_buffer;
831 };
832 };
833 }
834 while (Ustrstr(av_buffer, "OK\tScan ok.") == NULL);
f1e894f3 835 (void)close(sock);
8523533c
TK
836 }
837 /* ----------------------------------------------------------------------- */
838
839 /* "kavdaemon" scanner type ------------------------------------------------ */
840 else if (strcmpic(scanner_name,US"kavdaemon") == 0) {
841 uschar *kav_options;
842 uschar kav_options_buffer[1024];
843 uschar kav_options_default[] = "/var/run/AvpCtl";
844 struct sockaddr_un server;
845 int sock;
846 time_t t;
847 uschar tmpbuf[1024];
848 uschar scanrequest[1024];
849 uschar kav_match_string[128];
850 int kav_rc;
851 unsigned long kav_reportlen, bread;
852 pcre *kav_re;
8544e77a
PP
853 uschar *p;
854 int fits;
8e669ac1 855
8523533c
TK
856 if ((kav_options = string_nextinlist(&av_scanner_work, &sep,
857 kav_options_buffer,
858 sizeof(kav_options_buffer))) == NULL) {
859 /* no options supplied, use default options */
860 kav_options = kav_options_default;
861 };
8e669ac1 862
8523533c
TK
863 /* open the kavdaemon socket */
864 sock = socket(AF_UNIX, SOCK_STREAM, 0);
865 if (sock < 0) {
866 log_write(0, LOG_MAIN|LOG_PANIC,
867 "malware acl condition: can't open UNIX socket.");
8e669ac1 868 return DEFER;
8523533c
TK
869 }
870 server.sun_family = AF_UNIX;
871 Ustrcpy(server.sun_path, kav_options);
872 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
f1e894f3 873 (void)close(sock);
8523533c
TK
874 log_write(0, LOG_MAIN|LOG_PANIC,
875 "malware acl condition: unable to connect to kavdaemon UNIX socket (%s). errno=%d", kav_options, errno);
876 return DEFER;
877 }
8e669ac1 878
8523533c
TK
879 /* get current date and time, build scan request */
880 time(&t);
8544e77a
PP
881 /* pdp note: before the eml_filename parameter, this scanned the
882 directory; not finding documentation, so we'll strip off the directory.
883 The side-effect is that the test framework scanning may end up in
884 scanning more than was requested, but for the normal interface, this is
885 fine. */
886 strftime(CS tmpbuf, sizeof(tmpbuf), "<0>%d %b %H:%M:%S:%%s", localtime(&t));
887 fits = string_format(scanrequest, 1024,CS tmpbuf, eml_filename);
888 if (!fits) {
889 (void)close(sock);
890 log_write(0, LOG_MAIN|LOG_PANIC,
891 "malware filename does not fit in buffer [malware_internal() kavdaemon]");
892 }
893 p = strrchr(scanrequest, '/');
894 if (p)
895 *p = '\0';
896
897 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
898 scanner_name, kav_options);
8e669ac1 899
8523533c
TK
900 /* send scan request */
901 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
f1e894f3 902 (void)close(sock);
8523533c
TK
903 log_write(0, LOG_MAIN|LOG_PANIC,
904 "malware acl condition: unable to write to kavdaemon UNIX socket (%s)", kav_options);
905 return DEFER;
906 }
8e669ac1 907
8523533c
TK
908 /* wait for result */
909 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
f1e894f3 910 (void)close(sock);
8523533c
TK
911 log_write(0, LOG_MAIN|LOG_PANIC,
912 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
913 return DEFER;
914 }
8e669ac1 915
8523533c
TK
916 /* get errorcode from one nibble */
917 if (test_byte_order() == LITTLE_MY_ENDIAN) {
918 kav_rc = tmpbuf[0] & 0x0F;
919 }
920 else {
921 kav_rc = tmpbuf[1] & 0x0F;
922 };
8e669ac1 923
8523533c
TK
924 /* improper kavdaemon configuration */
925 if ( (kav_rc == 5) || (kav_rc == 6) ) {
f1e894f3 926 (void)close(sock);
8523533c
TK
927 log_write(0, LOG_MAIN|LOG_PANIC,
928 "malware acl condition: please reconfigure kavdaemon to NOT disinfect or remove infected files.");
929 return DEFER;
930 };
8e669ac1 931
8523533c 932 if (kav_rc == 1) {
f1e894f3 933 (void)close(sock);
8523533c
TK
934 log_write(0, LOG_MAIN|LOG_PANIC,
935 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
936 return DEFER;
937 };
8e669ac1 938
8523533c 939 if (kav_rc == 7) {
f1e894f3 940 (void)close(sock);
8523533c
TK
941 log_write(0, LOG_MAIN|LOG_PANIC,
942 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
943 return DEFER;
944 };
8e669ac1 945
8523533c
TK
946 /* code 8 is not handled, since it is ambigous. It appears mostly on
947 bounces where part of a file has been cut off */
8e669ac1 948
8523533c
TK
949 /* "virus found" return codes (2-4) */
950 if ((kav_rc > 1) && (kav_rc < 5)) {
951 int report_flag = 0;
8e669ac1 952
8523533c
TK
953 /* setup default virus name */
954 Ustrcpy(malware_name_buffer,"unknown");
955 malware_name = malware_name_buffer;
8e669ac1 956
8523533c
TK
957 if (test_byte_order() == LITTLE_MY_ENDIAN) {
958 report_flag = tmpbuf[1];
959 }
960 else {
961 report_flag = tmpbuf[0];
962 };
8e669ac1 963
8523533c
TK
964 /* read the report, if available */
965 if( report_flag == 1 ) {
966 /* read report size */
967 if ((bread = recv(sock, &kav_reportlen, 4, 0)) != 4) {
f1e894f3 968 (void)close(sock);
8523533c
TK
969 log_write(0, LOG_MAIN|LOG_PANIC,
970 "malware acl condition: cannot read report size from kavdaemon");
971 return DEFER;
972 };
8e669ac1 973
8523533c
TK
974 /* it's possible that avp returns av_buffer[1] == 1 but the
975 reportsize is 0 (!?) */
976 if (kav_reportlen > 0) {
977 /* set up match regex, depends on retcode */
978 if( kav_rc == 3 )
979 Ustrcpy(kav_match_string, "suspicion:\\s*(.+?)\\s*$");
980 else
981 Ustrcpy(kav_match_string, "infected:\\s*(.+?)\\s*$");
8e669ac1 982
8523533c
TK
983 kav_re = pcre_compile( CS kav_match_string,
984 PCRE_COPT,
985 (const char **)&rerror,
986 &roffset,
987 NULL );
8e669ac1
PH
988
989 /* read report, linewise */
8523533c
TK
990 while (kav_reportlen > 0) {
991 int result = 0;
992 int ovector[30];
8e669ac1 993
8523533c
TK
994 bread = 0;
995 while ( recv(sock, &tmpbuf[bread], 1, 0) == 1 ) {
996 kav_reportlen--;
997 if ( (tmpbuf[bread] == '\n') || (bread > 1021) ) break;
998 bread++;
999 };
1000 bread++;
1001 tmpbuf[bread] = '\0';
8e669ac1 1002
8523533c
TK
1003 /* try matcher on the line, grab substring */
1004 result = pcre_exec(kav_re, NULL, CS tmpbuf, Ustrlen(tmpbuf), 0, 0, ovector, 30);
1005 if (result >= 2) {
1006 pcre_copy_substring(CS tmpbuf, ovector, result, 1, CS malware_name_buffer, 255);
1007 break;
1008 };
1009 };
1010 };
1011 };
1012 }
1013 else {
1014 /* no virus found */
1015 malware_name = NULL;
1016 };
8e669ac1 1017
f1e894f3 1018 (void)close(sock);
8523533c
TK
1019 }
1020 /* ----------------------------------------------------------------------- */
8e669ac1
PH
1021
1022
8523533c
TK
1023 /* "cmdline" scanner type ------------------------------------------------ */
1024 else if (strcmpic(scanner_name,US"cmdline") == 0) {
1025 uschar *cmdline_scanner;
1026 uschar cmdline_scanner_buffer[1024];
1027 uschar *cmdline_trigger;
1028 uschar cmdline_trigger_buffer[1024];
1029 const pcre *cmdline_trigger_re;
1030 uschar *cmdline_regex;
1031 uschar cmdline_regex_buffer[1024];
1032 const pcre *cmdline_regex_re;
1033 uschar file_name[1024];
1034 uschar commandline[1024];
1035 void (*eximsigchld)(int);
1036 void (*eximsigpipe)(int);
1037 FILE *scanner_out = NULL;
1038 FILE *scanner_record = NULL;
1039 uschar linebuffer[32767];
1040 int trigger = 0;
1041 int result;
1042 int ovector[30];
8544e77a
PP
1043 uschar *p;
1044 BOOL fits;
8e669ac1 1045
8523533c
TK
1046 /* find scanner command line */
1047 if ((cmdline_scanner = string_nextinlist(&av_scanner_work, &sep,
1048 cmdline_scanner_buffer,
1049 sizeof(cmdline_scanner_buffer))) == NULL) {
1050 /* no command line supplied */
1051 log_write(0, LOG_MAIN|LOG_PANIC,
1052 "malware acl condition: missing commandline specification for cmdline scanner type.");
8e669ac1 1053 return DEFER;
8523533c 1054 };
8e669ac1 1055
8523533c
TK
1056 /* find scanner output trigger */
1057 if ((cmdline_trigger = string_nextinlist(&av_scanner_work, &sep,
1058 cmdline_trigger_buffer,
1059 sizeof(cmdline_trigger_buffer))) == NULL) {
1060 /* no trigger regex supplied */
1061 log_write(0, LOG_MAIN|LOG_PANIC,
1062 "malware acl condition: missing trigger specification for cmdline scanner type.");
8e669ac1 1063 return DEFER;
8523533c 1064 };
8e669ac1 1065
8523533c
TK
1066 /* precompile trigger regex */
1067 cmdline_trigger_re = pcre_compile(CS cmdline_trigger, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1068 if (cmdline_trigger_re == NULL) {
1069 log_write(0, LOG_MAIN|LOG_PANIC,
1070 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_trigger_re, rerror, roffset);
1071 return DEFER;
1072 };
8e669ac1 1073
8523533c
TK
1074 /* find scanner name regex */
1075 if ((cmdline_regex = string_nextinlist(&av_scanner_work, &sep,
1076 cmdline_regex_buffer,
1077 sizeof(cmdline_regex_buffer))) == NULL) {
1078 /* no name regex supplied */
1079 log_write(0, LOG_MAIN|LOG_PANIC,
1080 "malware acl condition: missing virus name regex specification for cmdline scanner type.");
8e669ac1 1081 return DEFER;
8523533c 1082 };
8e669ac1 1083
8523533c
TK
1084 /* precompile name regex */
1085 cmdline_regex_re = pcre_compile(CS cmdline_regex, PCRE_COPT, (const char **)&rerror, &roffset, NULL);
1086 if (cmdline_regex_re == NULL) {
1087 log_write(0, LOG_MAIN|LOG_PANIC,
1088 "malware acl condition: regular expression error in '%s': %s at offset %d", cmdline_regex_re, rerror, roffset);
1089 return DEFER;
1090 };
8e669ac1 1091
8544e77a
PP
1092 /* prepare scanner call; despite the naming, file_name holds a directory
1093 name which is documented as the value given to %s. */
1094 if (Ustrlen(eml_filename) > sizeof(file_name) - 1)
1095 {
1096 log_write(0, LOG_MAIN|LOG_PANIC,
1097 "malware filename does not fit in buffer [malware_internal() cmdline]");
1098 return DEFER;
1099 }
1100 p = strrchr(eml_filename, '/');
1101 if (p)
1102 *p = '\0';
1103 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1104 if (!fits)
1105 {
1106 log_write(0, LOG_MAIN|LOG_PANIC,
1107 "cmdline scanner command-line does not fit in buffer");
1108 return DEFER;
1109 }
1110
8523533c 1111 /* redirect STDERR too */
8544e77a
PP
1112 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1113 {
1114 log_write(0, LOG_MAIN|LOG_PANIC,
1115 "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1116 return DEFER;
1117 }
8523533c 1118 Ustrcat(commandline," 2>&1");
8e669ac1 1119
8544e77a
PP
1120 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1121
8523533c
TK
1122 /* store exims signal handlers */
1123 eximsigchld = signal(SIGCHLD,SIG_DFL);
1124 eximsigpipe = signal(SIGPIPE,SIG_DFL);
8e669ac1 1125
8523533c
TK
1126 scanner_out = popen(CS commandline,"r");
1127 if (scanner_out == NULL) {
1128 log_write(0, LOG_MAIN|LOG_PANIC,
1129 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1130 signal(SIGCHLD,eximsigchld);
1131 signal(SIGPIPE,eximsigpipe);
1132 return DEFER;
1133 };
8e669ac1 1134
b07e6aa3 1135 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
2632889e 1136 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
8e669ac1 1137
8523533c
TK
1138 if (scanner_record == NULL) {
1139 log_write(0, LOG_MAIN|LOG_PANIC,
1140 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1141 pclose(scanner_out);
1142 signal(SIGCHLD,eximsigchld);
1143 signal(SIGPIPE,eximsigpipe);
1144 return DEFER;
1145 };
8e669ac1 1146
8523533c
TK
1147 /* look for trigger while recording output */
1148 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1149 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1150 /* short write */
1151 log_write(0, LOG_MAIN|LOG_PANIC,
1152 "malware acl condition: short write on scanner output file (%s).", file_name);
1153 pclose(scanner_out);
1154 signal(SIGCHLD,eximsigchld);
1155 signal(SIGPIPE,eximsigpipe);
1156 return DEFER;
1157 };
1158 /* try trigger match */
1159 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1160 trigger = 1;
1161 };
8e669ac1 1162
f1e894f3 1163 (void)fclose(scanner_record);
8523533c
TK
1164 pclose(scanner_out);
1165 signal(SIGCHLD,eximsigchld);
1166 signal(SIGPIPE,eximsigpipe);
8e669ac1 1167
8523533c
TK
1168 if (trigger) {
1169 /* setup default virus name */
1170 Ustrcpy(malware_name_buffer,"unknown");
1171 malware_name = malware_name_buffer;
8e669ac1 1172
8523533c 1173 /* re-open the scanner output file, look for name match */
c58b88df 1174 scanner_record = fopen(CS file_name,"rb");
8523533c
TK
1175 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1176 /* try match */
1177 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1178 if (result >= 2) {
1179 pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1180 };
1181 };
f1e894f3 1182 (void)fclose(scanner_record);
8523533c
TK
1183 }
1184 else {
1185 /* no virus found */
1186 malware_name = NULL;
1187 };
1188 }
1189 /* ----------------------------------------------------------------------- */
8e669ac1
PH
1190
1191
8523533c
TK
1192 /* "sophie" scanner type ------------------------------------------------- */
1193 else if (strcmpic(scanner_name,US"sophie") == 0) {
1194 uschar *sophie_options;
1195 uschar sophie_options_buffer[1024];
1196 uschar sophie_options_default[] = "/var/run/sophie";
1197 int bread = 0;
1198 struct sockaddr_un server;
8544e77a
PP
1199 int sock, len;
1200 uschar *p;
1201 BOOL fits;
8523533c
TK
1202 uschar file_name[1024];
1203 uschar av_buffer[1024];
8e669ac1 1204
8523533c
TK
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 };
8e669ac1 1211
8523533c
TK
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.");
8e669ac1 1217 return DEFER;
8523533c
TK
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) {
f1e894f3 1222 (void)close(sock);
8523533c
TK
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 }
8e669ac1 1227
8523533c 1228 /* pass the scan directory to sophie */
8544e77a
PP
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 = strrchr(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
8523533c 1245 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
f1e894f3 1246 (void)close(sock);
8523533c
TK
1247 log_write(0, LOG_MAIN|LOG_PANIC,
1248 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
8e669ac1 1249 return DEFER;
8523533c 1250 };
8e669ac1 1251
f1e894f3 1252 (void)write(sock, "\n", 1);
8e669ac1 1253
8523533c
TK
1254 /* wait for result */
1255 memset(av_buffer, 0, sizeof(av_buffer));
a9ccd69a 1256 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
f1e894f3 1257 (void)close(sock);
8523533c
TK
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 };
8e669ac1 1262
f1e894f3 1263 (void)close(sock);
8e669ac1 1264
8523533c
TK
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 /* ----------------------------------------------------------------------- */
8e669ac1 1282
8523533c
TK
1283
1284 /* "clamd" scanner type ------------------------------------------------- */
8544e77a
PP
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. */
8523533c
TK
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;
f7b63901 1302 int sock,bread=0;
8e669ac1 1303 unsigned int port;
8523533c
TK
1304 uschar file_name[1024];
1305 uschar av_buffer[1024];
1306 uschar hostname[256];
1307 struct hostent *he;
1308 struct in_addr in;
2376f2f5
TK
1309 uschar *clamd_options2;
1310 uschar clamd_options2_buffer[1024];
1311 uschar clamd_options2_default[] = "";
2376f2f5
TK
1312 uschar *clamav_fbuf;
1313 uschar scanrequest[1024];
1314 int sockData, clam_fd, result;
1315 unsigned int fsize;
8544e77a
PP
1316 BOOL use_scan_command, fits;
1317#ifdef WITH_OLD_CLAMAV_STREAM
1318 uschar av_buffer2[1024];
1319#else
1320 uint32_t send_size, send_final_zeroblock;
1321#endif
8523533c
TK
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 }
2376f2f5
TK
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 }
8e669ac1 1334
8544e77a
PP
1335 if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
1336 use_scan_command = TRUE;
1337 else
1338 use_scan_command = FALSE;
1339
8523533c
TK
1340 /* socket does not start with '/' -> network socket */
1341 if (*clamd_options != '/') {
8e669ac1 1342
8544e77a
PP
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
8523533c
TK
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 };
8e669ac1 1353
8523533c
TK
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 }
8e669ac1 1360
8523533c 1361 in = *(struct in_addr *) he->h_addr_list[0];
8e669ac1 1362
8523533c
TK
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 }
8e669ac1 1370
8523533c 1371 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
f1e894f3 1372 (void)close(sock);
8523533c
TK
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 }
2376f2f5 1378
8544e77a
PP
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 }
2376f2f5 1387
8544e77a
PP
1388 server.sun_family = AF_UNIX;
1389 Ustrcpy(server.sun_path, clamd_options);
8e669ac1 1390
8544e77a
PP
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 }
8e669ac1 1399
8544e77a
PP
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. */
2376f2f5 1404
8544e77a 1405 if (!use_scan_command) {
2376f2f5 1406
8544e77a
PP
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. */
2376f2f5 1411
8544e77a
PP
1412 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1413 scanner_name);
8e669ac1 1414
8544e77a
PP
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);
8e669ac1 1424
8544e77a
PP
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 }
8e669ac1 1432
8544e77a
PP
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 }
8e669ac1 1439
8544e77a
PP
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 }
8e669ac1 1446
8544e77a
PP
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 };
2376f2f5 1454
8544e77a
PP
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 }
384152a6 1462
8544e77a
PP
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 }
384152a6 1470
8544e77a
PP
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. */
384152a6 1476
8544e77a
PP
1477 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1478 scanner_name);
384152a6 1479
8544e77a
PP
1480 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1481 if (send(sock, "zINSTREAM", 10, 0) < 0) {
8523533c 1482 log_write(0, LOG_MAIN|LOG_PANIC,
8544e77a
PP
1483 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1484 strerror(errno));
1485 (void)close(sock);
8523533c
TK
1486 return DEFER;
1487 }
8e669ac1 1488
8544e77a
PP
1489#define CLOSE_SOCKDATA /**/
1490#endif
8e669ac1 1491
8544e77a
PP
1492 /* calc file size */
1493 clam_fd = open(CS eml_filename, O_RDONLY);
1494 if (clam_fd == -1) {
8523533c 1495 log_write(0, LOG_MAIN|LOG_PANIC,
8544e77a
PP
1496 "malware acl condition: clamd: can't open spool file %s: %s",
1497 eml_filename, strerror(errno));
1498 CLOSE_SOCKDATA; (void)close(sock);
8523533c
TK
1499 return DEFER;
1500 }
8544e77a
PP
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);
8e669ac1 1510
8544e77a
PP
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 }
8e669ac1 1519
8544e77a
PP
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);
8e669ac1 1530
8544e77a
PP
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
8e669ac1 1554
8544e77a
PP
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 }
8e669ac1 1582
8544e77a
PP
1583 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1584 scanner_name, clamd_options);
8e669ac1 1585
8544e77a
PP
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 }
8e669ac1 1592
8544e77a
PP
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. */
8e669ac1 1598
8523533c
TK
1599 /* Read the result */
1600 memset(av_buffer, 0, sizeof(av_buffer));
a9ccd69a 1601 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
f1e894f3 1602 (void)close(sock);
8523533c
TK
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 }
8e669ac1 1610
8523533c
TK
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 }
8e669ac1 1616
8523533c
TK
1617 /* Check the result. ClamAV Returns
1618 infected: -> "<filename>: <virusname> FOUND"
1619 not-infected: -> "<filename>: OK"
8544e77a 1620 error: -> "<filename>: <errcode> ERROR */
8e669ac1 1621
8523533c
TK
1622 if (!(*av_buffer)) {
1623 log_write(0, LOG_MAIN|LOG_PANIC,
1624 "malware acl condition: clamd: ClamAV returned null");
1625 return DEFER;
1626 }
8e669ac1 1627
8544e77a 1628 /* strip newline at the end (won't be present for zINSTREAM) */
3c5cd616
TK
1629 p = av_buffer + Ustrlen(av_buffer) - 1;
1630 if( *p == '\n' ) *p = '\0';
1631
8544e77a
PP
1632 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1633
8523533c
TK
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",
8e669ac1 1638 av_buffer);
8523533c
TK
1639 return DEFER;
1640 }
8e669ac1 1641
3c5cd616 1642 /* strip filename */
8523533c
TK
1643 ++p;
1644 while (*p == ' ') ++p;
1645 vname = p;
8523533c
TK
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;
8544e77a 1652 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
8523533c
TK
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;
8544e77a 1668 DEBUG(D_acl) debug_printf("Malware not found\n");
8523533c
TK
1669 }
1670 }
1671 }
1672 /* ----------------------------------------------------------------------- */
8e669ac1
PH
1673
1674
8523533c
TK
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;
8e669ac1 1684
8523533c
TK
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);
384152a6
TK
1689 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1690 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
8523533c
TK
1691 log_write(0, LOG_MAIN|LOG_PANIC,
1692 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1693 return DEFER;
1694 }
1695 }
8e669ac1 1696
8523533c
TK
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.");
8e669ac1 1702 return DEFER;
8523533c
TK
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) {
f1e894f3 1707 (void)close(sock);
8523533c
TK
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 }
8e669ac1 1712
8523533c 1713 malware_name = NULL;
8e669ac1 1714
8544e77a
PP
1715 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1716
1717 retval = mksd_scan_packed(sock, eml_filename);
8e669ac1 1718
8523533c
TK
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 /* ----------------------------------------------------------------------- */
8e669ac1 1731
8523533c
TK
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)) ) {
8544e77a 1739 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
8523533c
TK
1740 return OK;
1741 }
1742 else {
1743 return FAIL;
1744 };
1745}
1746
1747
1748/* simple wrapper for reading lines from sockets */
1749int 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
8544e77a 1769static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
8523533c
TK
1770{
1771 int i;
8e669ac1 1772
8523533c
TK
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 }
8e669ac1 1783
8523533c
TK
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
8544e77a 1798static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
8523533c
TK
1799{
1800 int offset = 0;
1801 int i;
8e669ac1 1802
8523533c
TK
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 }
8e669ac1 1810
8523533c
TK
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');
8e669ac1 1820
8523533c
TK
1821 av_buffer[offset] = '\0';
1822 return offset;
1823}
1824
8544e77a 1825static int mksd_parse_line (char *line)
8523533c
TK
1826{
1827 char *p;
8e669ac1 1828
8523533c
TK
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);
384152a6 1849 malware_name = malware_name_buffer;
8523533c
TK
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
8544e77a 1859static int mksd_scan_packed(int sock, uschar *scan_filename)
8523533c 1860{
8544e77a
PP
1861 struct iovec iov[3];
1862 char *cmd = "MSQ\n";
8523533c 1863 uschar av_buffer[1024];
8e669ac1 1864
8523533c
TK
1865 iov[0].iov_base = cmd;
1866 iov[0].iov_len = 3;
8544e77a
PP
1867 iov[1].iov_base = CS scan_filename;
1868 iov[1].iov_len = Ustrlen(scan_filename);
8523533c 1869 iov[2].iov_base = cmd + 3;
8544e77a
PP
1870 iov[2].iov_len = 1;
1871
1872 if (mksd_writev (sock, iov, 3) < 0)
8523533c 1873 return DEFER;
8e669ac1 1874
8523533c
TK
1875 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1876 return DEFER;
8e669ac1 1877
8523533c 1878 close (sock);
8e669ac1 1879
8523533c
TK
1880 return mksd_parse_line (CS av_buffer);
1881}
1882
1883#endif