Merge branch 'tom_dev'
[exim.git] / src / src / malware.c
CommitLineData
73a46702 1/* $Cambridge: exim/src/src/malware.c,v 1.21 2010/06/07 00:12:42 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*/
dbc4b90d
PP
103int
104malware_in_file(uschar *eml_filename) {
8544e77a
PP
105 uschar *scan_options[2];
106 uschar message_id_buf[64];
107 int ret;
108
73a46702 109 scan_options[0] = US"*";
8544e77a
PP
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),
73a46702 115 "dummy-%d", pseudo_random_number(INT_MAX));
8544e77a 116 message_id = message_id_buf;
73a46702
PP
117 sender_address = US"malware-sender@example.net";
118 return_path = US"";
8544e77a 119 recipients_list = NULL;
73a46702 120 receive_add_recipient(US"malware-victim@example.net", -1);
8544e77a
PP
121 enable_dollar_recipients = TRUE;
122
123 ret = malware_internal(scan_options, eml_filename, TRUE);
124
73a46702 125 Ustrncpy(spooled_message_id, message_id, sizeof(spooled_message_id));
8544e77a
PP
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
140is via malware(), or there's malware_in_file() used for testing/debugging.
141
142Arguments:
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
147Returns: Exim message processing code (OK, FAIL, DEFER, ...)
148 where true means malware was found (condition applies)
149*/
150static int malware_internal(uschar **listptr, uschar *eml_filename, BOOL faking) {
8523533c
TK
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[] = ".+";
f7b63901 159 unsigned long mbox_size;
8523533c
TK
160 FILE *mbox_file;
161 int roffset;
162 const pcre *re;
163 const uschar *rerror;
8e669ac1 164
8523533c 165 /* make sure the eml mbox file is spooled up */
8544e77a 166 mbox_file = spool_mbox(&mbox_size, faking ? eml_filename : NULL);
8523533c
TK
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");
8e669ac1 171 return DEFER;
8523533c
TK
172 };
173 /* none of our current scanners need the mbox
174 file as a stream, so we can close it right away */
f1e894f3 175 (void)fclose(mbox_file);
8e669ac1 176
8523533c
TK
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) {
8e669ac1 181
8523533c 182 /* parse 1st option */
8e669ac1 183 if ( (strcmpic(malware_regex,US"false") == 0) ||
8523533c
TK
184 (Ustrcmp(malware_regex,"0") == 0) ) {
185 /* explicitly no matching */
186 return FAIL;
187 };
8e669ac1 188
8523533c 189 /* special cases (match anything except empty) */
8e669ac1
PH
190 if ( (strcmpic(malware_regex,US"true") == 0) ||
191 (Ustrcmp(malware_regex,"*") == 0) ||
8523533c
TK
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
ee873c8c
TK
201 /* Reset sep that is set by previous string_nextinlist() call */
202 sep = 0;
203
8523533c
TK
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 };
8e669ac1 240
e5496194
TK
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
8544e77a
PP
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);
e5496194
TK
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>")) ) {
7156b1ef 333 if ((strhelper2 = Ustrstr(buf, US"</name>")) != NULL) {
e5496194
TK
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 }
384152a6
TK
348 /* "drweb" scanner type ----------------------------------------------- */
349 /* v0.1 - added support for tcp sockets */
350 /* v0.0 - initial release -- support for unix sockets */
e5496194 351 else if (strcmpic(scanner_name,US"drweb") == 0) {
384152a6
TK
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;
384152a6
TK
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) {
f1e894f3 401 (void)close(sock);
384152a6
TK
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);
384152a6
TK
411
412 /* calc file size */
8544e77a 413 drweb_fd = open(CS eml_filename, O_RDONLY);
384152a6 414 if (drweb_fd == -1) {
f1e894f3 415 (void)close(sock);
384152a6
TK
416 log_write(0, LOG_MAIN|LOG_PANIC,
417 "malware acl condition: drweb: can't open spool file %s: %s",
8544e77a 418 eml_filename, strerror(errno));
384152a6
TK
419 return DEFER;
420 }
421 fsize = lseek(drweb_fd, 0, SEEK_END);
422 if (fsize == -1) {
f1e894f3
PH
423 (void)close(sock);
424 (void)close(drweb_fd);
384152a6
TK
425 log_write(0, LOG_MAIN|LOG_PANIC,
426 "malware acl condition: drweb: can't seek spool file %s: %s",
8544e77a 427 eml_filename, strerror(errno));
384152a6
TK
428 return DEFER;
429 }
430 drweb_slen = htonl(fsize);
431 lseek(drweb_fd, 0, SEEK_SET);
432
8544e77a
PP
433 DEBUG(D_acl) debug_printf("Malware scan: issuing %s remote scan [%s %u]\n",
434 scanner_name, hostname, port);
435
384152a6
TK
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)) {
f1e894f3
PH
441 (void)close(sock);
442 (void)close(drweb_fd);
384152a6
TK
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) {
f1e894f3
PH
450 (void)close(sock);
451 (void)close(drweb_fd);
384152a6
TK
452 log_write(0, LOG_MAIN|LOG_PANIC,
453 "malware acl condition: drweb: unable to allocate memory %u for file (%s)",
8544e77a 454 fsize, eml_filename);
384152a6
TK
455 return DEFER;
456 }
457
458 result = read (drweb_fd, drweb_fbuf, fsize);
459 if (result == -1) {
f1e894f3
PH
460 (void)close(sock);
461 (void)close(drweb_fd);
384152a6
TK
462 free(drweb_fbuf);
463 log_write(0, LOG_MAIN|LOG_PANIC,
464 "malware acl condition: drweb: can't read spool file %s: %s",
8544e77a 465 eml_filename, strerror(errno));
384152a6
TK
466 return DEFER;
467 }
f1e894f3 468 (void)close(drweb_fd);
384152a6
TK
469
470 /* send file body to socket */
471 if (send(sock, drweb_fbuf, fsize, 0) < 0) {
f1e894f3 472 (void)close(sock);
384152a6
TK
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 }
f1e894f3 478 (void)close(drweb_fd);
384152a6
TK
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) {
f1e894f3 491 (void)close(sock);
384152a6
TK
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);
8544e77a
PP
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);
384152a6
TK
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) ||
8544e77a 509 (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) ||
384152a6 510 (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) {
f1e894f3 511 (void)close(sock);
384152a6
TK
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))) {
f1e894f3 520 (void)close(sock);
384152a6
TK
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))) {
f1e894f3 528 (void)close(sock);
384152a6
TK
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))) {
f1e894f3 551 (void)close(sock);
384152a6
TK
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) {
f1e894f3 560 (void)close(sock);
384152a6
TK
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 {
1ba28e2b 595 const char *drweb_s = NULL;
384152a6
TK
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);
f1e894f3 608 (void)close(sock);
384152a6
TK
609 return DEFER;
610 }
611 /* no virus found */
612 malware_name = NULL;
613 };
f1e894f3 614 (void)close(sock);
384152a6
TK
615 }
616 /* ----------------------------------------------------------------------- */
8523533c
TK
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];
8523533c
TK
622 struct sockaddr_un server;
623 int sock;
7e274b4b 624 int result;
8e669ac1 625
8523533c
TK
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 };
8e669ac1 632
8523533c
TK
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.");
8e669ac1 638 return DEFER;
8523533c
TK
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) {
f1e894f3 643 (void)close(sock);
8523533c
TK
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 }
8e669ac1 648
8523533c
TK
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 */
f1e894f3 654 (void)close(sock);
8523533c
TK
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 };
8e669ac1 659
8523533c 660 /* prepare our command */
8544e77a
PP
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);
8e669ac1 664
8523533c
TK
665 /* and send it */
666 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
f1e894f3 667 (void)close(sock);
8523533c
TK
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 }
8e669ac1 672
8523533c 673 malware_name = NULL;
7e274b4b 674 result = 0;
8523533c
TK
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);
7e274b4b
TK
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,
8544e77a
PP
683 "malware acl condition: unable to scan file %s (Responded: %s).",
684 eml_filename, buf);
7e274b4b
TK
685 result = DEFER;
686 break;
687 } else if (Ustrncmp(buf,"322",3) == 0) {
8523533c
TK
688 uschar *p = Ustrchr(&buf[4],' ');
689 *p = '\0';
690 Ustrcpy(malware_name_buffer,&buf[4]);
691 malware_name = malware_name_buffer;
7e274b4b
TK
692 };
693 }
694
695 /* prepare our command */
b07e6aa3 696 (void)string_format(buf, 32768, "quit\r\n");
7e274b4b
TK
697
698 /* and send it */
699 if (send(sock, buf, Ustrlen(buf), 0) < 0) {
f1e894f3 700 (void)close(sock);
7e274b4b
TK
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;
8523533c
TK
704 }
705
7e274b4b
TK
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 */
f1e894f3 711 (void)close(sock);
7e274b4b
TK
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
f1e894f3 717 (void)close(sock);
7e274b4b
TK
718
719 if (result == DEFER) return DEFER;
8523533c
TK
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;
f7b63901
PH
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" };
8e669ac1 735
8523533c
TK
736 malware_name = NULL;
737 if ((fsecure_options = string_nextinlist(&av_scanner_work, &sep,
738 fsecure_options_buffer,
8e669ac1 739 sizeof(fsecure_options_buffer))) == NULL) {
8523533c
TK
740 /* no options supplied, use default options */
741 fsecure_options = fsecure_options_default;
742 };
8e669ac1 743
8523533c
TK
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) {
f1e894f3 755 (void)close(sock);
8523533c
TK
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 }
8e669ac1 761
8544e77a
PP
762 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
763 scanner_name, fsecure_options);
764
8523533c
TK
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) {
f1e894f3 770 (void)close(sock);
8523533c
TK
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));
8e669ac1 774 return DEFER;
8523533c 775 };
8e669ac1 776
a9ccd69a 777 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
8523533c
TK
778 if (bread >0) av_buffer[bread]='\0';
779 if (bread < 0) {
f1e894f3 780 (void)close(sock);
8523533c
TK
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 };
8e669ac1 789
8523533c 790 /* pass the mailfile to fsecure */
8544e77a 791 (void)string_format(file_name,1024,"SCAN\t%s\n", eml_filename);
8523533c
TK
792 /* debug_printf("send scan %s",file_name); */
793 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
f1e894f3 794 (void)close(sock);
8523533c
TK
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 };
8e669ac1 800
8523533c
TK
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);
8e669ac1 804
8523533c
TK
805 /* read report, linewise */
806 do {
807 int ovector[30];
808 i = 0;
809 memset(av_buffer, 0, sizeof(av_buffer));
810 do {
a9ccd69a 811 bread=ip_recv(sock, &av_buffer[i], 1, MALWARE_TIMEOUT);
8523533c 812 if (bread < 0) {
f1e894f3 813 (void)close(sock);
8523533c
TK
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); */
8e669ac1 823
8523533c
TK
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);
f1e894f3 836 (void)close(sock);
8523533c
TK
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;
8544e77a
PP
854 uschar *p;
855 int fits;
8e669ac1 856
8523533c
TK
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 };
8e669ac1 863
8523533c
TK
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.");
8e669ac1 869 return DEFER;
8523533c
TK
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) {
f1e894f3 874 (void)close(sock);
8523533c
TK
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 }
8e669ac1 879
8523533c
TK
880 /* get current date and time, build scan request */
881 time(&t);
8544e77a
PP
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 }
73a46702 894 p = Ustrrchr(scanrequest, '/');
8544e77a
PP
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);
8e669ac1 900
8523533c
TK
901 /* send scan request */
902 if (send(sock, scanrequest, Ustrlen(scanrequest)+1, 0) < 0) {
f1e894f3 903 (void)close(sock);
8523533c
TK
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 }
8e669ac1 908
8523533c
TK
909 /* wait for result */
910 if ((bread = recv(sock, tmpbuf, 2, 0) != 2)) {
f1e894f3 911 (void)close(sock);
8523533c
TK
912 log_write(0, LOG_MAIN|LOG_PANIC,
913 "malware acl condition: unable to read 2 bytes from kavdaemon socket.");
914 return DEFER;
915 }
8e669ac1 916
8523533c
TK
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 };
8e669ac1 924
8523533c
TK
925 /* improper kavdaemon configuration */
926 if ( (kav_rc == 5) || (kav_rc == 6) ) {
f1e894f3 927 (void)close(sock);
8523533c
TK
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 };
8e669ac1 932
8523533c 933 if (kav_rc == 1) {
f1e894f3 934 (void)close(sock);
8523533c
TK
935 log_write(0, LOG_MAIN|LOG_PANIC,
936 "malware acl condition: kavdaemon reported 'scanning not completed' (code 1).");
937 return DEFER;
938 };
8e669ac1 939
8523533c 940 if (kav_rc == 7) {
f1e894f3 941 (void)close(sock);
8523533c
TK
942 log_write(0, LOG_MAIN|LOG_PANIC,
943 "malware acl condition: kavdaemon reported 'kavdaemon damaged' (code 7).");
944 return DEFER;
945 };
8e669ac1 946
8523533c
TK
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 */
8e669ac1 949
8523533c
TK
950 /* "virus found" return codes (2-4) */
951 if ((kav_rc > 1) && (kav_rc < 5)) {
952 int report_flag = 0;
8e669ac1 953
8523533c
TK
954 /* setup default virus name */
955 Ustrcpy(malware_name_buffer,"unknown");
956 malware_name = malware_name_buffer;
8e669ac1 957
8523533c
TK
958 if (test_byte_order() == LITTLE_MY_ENDIAN) {
959 report_flag = tmpbuf[1];
960 }
961 else {
962 report_flag = tmpbuf[0];
963 };
8e669ac1 964
8523533c
TK
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) {
f1e894f3 969 (void)close(sock);
8523533c
TK
970 log_write(0, LOG_MAIN|LOG_PANIC,
971 "malware acl condition: cannot read report size from kavdaemon");
972 return DEFER;
973 };
8e669ac1 974
8523533c
TK
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*$");
8e669ac1 983
8523533c
TK
984 kav_re = pcre_compile( CS kav_match_string,
985 PCRE_COPT,
986 (const char **)&rerror,
987 &roffset,
988 NULL );
8e669ac1
PH
989
990 /* read report, linewise */
8523533c
TK
991 while (kav_reportlen > 0) {
992 int result = 0;
993 int ovector[30];
8e669ac1 994
8523533c
TK
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';
8e669ac1 1003
8523533c
TK
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 };
8e669ac1 1018
f1e894f3 1019 (void)close(sock);
8523533c
TK
1020 }
1021 /* ----------------------------------------------------------------------- */
8e669ac1
PH
1022
1023
8523533c
TK
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];
8544e77a
PP
1044 uschar *p;
1045 BOOL fits;
8e669ac1 1046
8523533c
TK
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.");
8e669ac1 1054 return DEFER;
8523533c 1055 };
8e669ac1 1056
8523533c
TK
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.");
8e669ac1 1064 return DEFER;
8523533c 1065 };
8e669ac1 1066
8523533c
TK
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 };
8e669ac1 1074
8523533c
TK
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.");
8e669ac1 1082 return DEFER;
8523533c 1083 };
8e669ac1 1084
8523533c
TK
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 };
8e669ac1 1092
8544e77a
PP
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 }
3346ab01
PP
1101 Ustrcpy(file_name, eml_filename);
1102 p = Ustrrchr(file_name, '/');
8544e77a
PP
1103 if (p)
1104 *p = '\0';
1105 fits = string_format(commandline, sizeof(commandline), CS cmdline_scanner, file_name);
1106 if (!fits)
1107 {
1108 log_write(0, LOG_MAIN|LOG_PANIC,
1109 "cmdline scanner command-line does not fit in buffer");
1110 return DEFER;
1111 }
1112
8523533c 1113 /* redirect STDERR too */
8544e77a
PP
1114 if (Ustrlen(commandline) + 5 > sizeof(commandline))
1115 {
1116 log_write(0, LOG_MAIN|LOG_PANIC,
1117 "cmdline scanner command-line does not fit in buffer (STDERR redirect)");
1118 return DEFER;
1119 }
8523533c 1120 Ustrcat(commandline," 2>&1");
8e669ac1 1121
8544e77a
PP
1122 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n", scanner_name, commandline);
1123
8523533c
TK
1124 /* store exims signal handlers */
1125 eximsigchld = signal(SIGCHLD,SIG_DFL);
1126 eximsigpipe = signal(SIGPIPE,SIG_DFL);
8e669ac1 1127
8523533c
TK
1128 scanner_out = popen(CS commandline,"r");
1129 if (scanner_out == NULL) {
1130 log_write(0, LOG_MAIN|LOG_PANIC,
1131 "malware acl condition: calling cmdline scanner (%s) failed: %s.", commandline, strerror(errno));
1132 signal(SIGCHLD,eximsigchld);
1133 signal(SIGPIPE,eximsigpipe);
1134 return DEFER;
1135 };
8e669ac1 1136
b07e6aa3 1137 (void)string_format(file_name,1024,"%s/scan/%s/%s_scanner_output", spool_directory, message_id, message_id);
2632889e 1138 scanner_record = modefopen(file_name,"wb",SPOOL_MODE);
8e669ac1 1139
8523533c
TK
1140 if (scanner_record == NULL) {
1141 log_write(0, LOG_MAIN|LOG_PANIC,
1142 "malware acl condition: opening scanner output file (%s) failed: %s.", file_name, strerror(errno));
1143 pclose(scanner_out);
1144 signal(SIGCHLD,eximsigchld);
1145 signal(SIGPIPE,eximsigpipe);
1146 return DEFER;
1147 };
8e669ac1 1148
8523533c
TK
1149 /* look for trigger while recording output */
1150 while(fgets(CS linebuffer,32767,scanner_out) != NULL) {
1151 if ( Ustrlen(linebuffer) > fwrite(linebuffer, 1, Ustrlen(linebuffer), scanner_record) ) {
1152 /* short write */
1153 log_write(0, LOG_MAIN|LOG_PANIC,
1154 "malware acl condition: short write on scanner output file (%s).", file_name);
1155 pclose(scanner_out);
1156 signal(SIGCHLD,eximsigchld);
1157 signal(SIGPIPE,eximsigpipe);
1158 return DEFER;
1159 };
1160 /* try trigger match */
1161 if (!trigger && regex_match_and_setup(cmdline_trigger_re, linebuffer, 0, -1))
1162 trigger = 1;
1163 };
8e669ac1 1164
f1e894f3 1165 (void)fclose(scanner_record);
8523533c
TK
1166 pclose(scanner_out);
1167 signal(SIGCHLD,eximsigchld);
1168 signal(SIGPIPE,eximsigpipe);
8e669ac1 1169
8523533c
TK
1170 if (trigger) {
1171 /* setup default virus name */
1172 Ustrcpy(malware_name_buffer,"unknown");
1173 malware_name = malware_name_buffer;
8e669ac1 1174
8523533c 1175 /* re-open the scanner output file, look for name match */
c58b88df 1176 scanner_record = fopen(CS file_name,"rb");
8523533c
TK
1177 while(fgets(CS linebuffer,32767,scanner_record) != NULL) {
1178 /* try match */
1179 result = pcre_exec(cmdline_regex_re, NULL, CS linebuffer, Ustrlen(linebuffer), 0, 0, ovector, 30);
1180 if (result >= 2) {
1181 pcre_copy_substring(CS linebuffer, ovector, result, 1, CS malware_name_buffer, 255);
1182 };
1183 };
f1e894f3 1184 (void)fclose(scanner_record);
8523533c
TK
1185 }
1186 else {
1187 /* no virus found */
1188 malware_name = NULL;
1189 };
1190 }
1191 /* ----------------------------------------------------------------------- */
8e669ac1
PH
1192
1193
8523533c
TK
1194 /* "sophie" scanner type ------------------------------------------------- */
1195 else if (strcmpic(scanner_name,US"sophie") == 0) {
1196 uschar *sophie_options;
1197 uschar sophie_options_buffer[1024];
1198 uschar sophie_options_default[] = "/var/run/sophie";
1199 int bread = 0;
1200 struct sockaddr_un server;
8544e77a
PP
1201 int sock, len;
1202 uschar *p;
8523533c
TK
1203 uschar file_name[1024];
1204 uschar av_buffer[1024];
8e669ac1 1205
8523533c
TK
1206 if ((sophie_options = string_nextinlist(&av_scanner_work, &sep,
1207 sophie_options_buffer,
1208 sizeof(sophie_options_buffer))) == NULL) {
1209 /* no options supplied, use default options */
1210 sophie_options = sophie_options_default;
1211 };
8e669ac1 1212
8523533c
TK
1213 /* open the sophie socket */
1214 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1215 if (sock < 0) {
1216 log_write(0, LOG_MAIN|LOG_PANIC,
1217 "malware acl condition: can't open UNIX socket.");
8e669ac1 1218 return DEFER;
8523533c
TK
1219 }
1220 server.sun_family = AF_UNIX;
1221 Ustrcpy(server.sun_path, sophie_options);
1222 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
f1e894f3 1223 (void)close(sock);
8523533c
TK
1224 log_write(0, LOG_MAIN|LOG_PANIC,
1225 "malware acl condition: unable to connect to sophie UNIX socket (%s). errno=%d", sophie_options, errno);
1226 return DEFER;
1227 }
8e669ac1 1228
8523533c 1229 /* pass the scan directory to sophie */
8544e77a
PP
1230 len = Ustrlen(eml_filename) + 1;
1231 if (len > sizeof(file_name))
1232 {
1233 (void)close(sock);
1234 log_write(0, LOG_MAIN|LOG_PANIC,
1235 "malware filename does not fit in buffer [malware_internal() sophie]");
1236 return DEFER;
1237 }
1238 memcpy(file_name, eml_filename, len);
73a46702 1239 p = Ustrrchr(file_name, '/');
8544e77a
PP
1240 if (p)
1241 *p = '\0';
1242
1243 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan [%s]\n",
1244 scanner_name, sophie_options);
1245
8523533c 1246 if (write(sock, file_name, Ustrlen(file_name)) < 0) {
f1e894f3 1247 (void)close(sock);
8523533c
TK
1248 log_write(0, LOG_MAIN|LOG_PANIC,
1249 "malware acl condition: unable to write to sophie UNIX socket (%s)", sophie_options);
8e669ac1 1250 return DEFER;
8523533c 1251 };
8e669ac1 1252
f1e894f3 1253 (void)write(sock, "\n", 1);
8e669ac1 1254
8523533c
TK
1255 /* wait for result */
1256 memset(av_buffer, 0, sizeof(av_buffer));
a9ccd69a 1257 if ((!(bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT)) > 0)) {
f1e894f3 1258 (void)close(sock);
8523533c
TK
1259 log_write(0, LOG_MAIN|LOG_PANIC,
1260 "malware acl condition: unable to read from sophie UNIX socket (%s)", sophie_options);
1261 return DEFER;
1262 };
8e669ac1 1263
f1e894f3 1264 (void)close(sock);
8e669ac1 1265
8523533c
TK
1266 /* infected ? */
1267 if (av_buffer[0] == '1') {
1268 if (Ustrchr(av_buffer, '\n')) *Ustrchr(av_buffer, '\n') = '\0';
1269 Ustrcpy(malware_name_buffer,&av_buffer[2]);
1270 malware_name = malware_name_buffer;
1271 }
1272 else if (!strncmp(CS av_buffer, "-1", 2)) {
1273 log_write(0, LOG_MAIN|LOG_PANIC,
1274 "malware acl condition: malware acl condition: sophie reported error");
1275 return DEFER;
1276 }
1277 else {
1278 /* all ok, no virus */
1279 malware_name = NULL;
1280 };
1281 }
1282 /* ----------------------------------------------------------------------- */
8e669ac1 1283
8523533c
TK
1284
1285 /* "clamd" scanner type ------------------------------------------------- */
8544e77a
PP
1286 /* This code was originally contributed by David Saez */
1287 /* There are three scanning methods available to us:
1288 * (1) Use the SCAN command, pointing to a file in the filesystem
1289 * (2) Use the STREAM command, send the data on a separate port
1290 * (3) Use the zINSTREAM command, send the data inline
1291 * The zINSTREAM command was introduced with ClamAV 0.95, which marked
1292 * STREAM deprecated; see: http://wiki.clamav.net/bin/view/Main/UpgradeNotes095
1293 * In Exim, we use SCAN if using a Unix-domain socket or explicitly told that
1294 * the TCP-connected daemon is actually local; otherwise we use zINSTREAM unless
1295 * WITH_OLD_CLAMAV_STREAM is defined.
1296 * See Exim bug 926 for details. */
8523533c
TK
1297 else if (strcmpic(scanner_name,US"clamd") == 0) {
1298 uschar *clamd_options;
1299 uschar clamd_options_buffer[1024];
1300 uschar clamd_options_default[] = "/tmp/clamd";
94a18f28 1301 uschar *p, *vname, *result_tag, *response_end;
8523533c 1302 struct sockaddr_un server;
f7b63901 1303 int sock,bread=0;
8e669ac1 1304 unsigned int port;
8523533c
TK
1305 uschar file_name[1024];
1306 uschar av_buffer[1024];
1307 uschar hostname[256];
1308 struct hostent *he;
1309 struct in_addr in;
2376f2f5
TK
1310 uschar *clamd_options2;
1311 uschar clamd_options2_buffer[1024];
1312 uschar clamd_options2_default[] = "";
2376f2f5 1313 uschar *clamav_fbuf;
73a46702 1314 int clam_fd, result;
2376f2f5 1315 unsigned int fsize;
8544e77a
PP
1316 BOOL use_scan_command, fits;
1317#ifdef WITH_OLD_CLAMAV_STREAM
1318 uschar av_buffer2[1024];
73a46702 1319 int sockData;
8544e77a
PP
1320#else
1321 uint32_t send_size, send_final_zeroblock;
1322#endif
8523533c
TK
1323
1324 if ((clamd_options = string_nextinlist(&av_scanner_work, &sep,
1325 clamd_options_buffer,
1326 sizeof(clamd_options_buffer))) == NULL) {
1327 /* no options supplied, use default options */
1328 clamd_options = clamd_options_default;
1329 }
2376f2f5
TK
1330 if ((clamd_options2 = string_nextinlist(&av_scanner_work, &sep,
1331 clamd_options2_buffer,
1332 sizeof(clamd_options2_buffer))) == NULL) {
1333 clamd_options2 = clamd_options2_default;
1334 }
8e669ac1 1335
8544e77a
PP
1336 if ((*clamd_options == '/') || (strcmpic(clamd_options2,US"local") == 0))
1337 use_scan_command = TRUE;
1338 else
1339 use_scan_command = FALSE;
1340
94a18f28
PP
1341 /* See the discussion of response formats below to see why we really don't
1342 like colons in filenames when passing filenames to ClamAV. */
1343 if (use_scan_command && Ustrchr(eml_filename, ':')) {
1344 log_write(0, LOG_MAIN|LOG_PANIC,
1345 "malware acl condition: clamd: local/SCAN mode incompatible with" \
1346 " : in path to email filename [%s]", eml_filename);
1347 return DEFER;
1348 }
1349
8523533c
TK
1350 /* socket does not start with '/' -> network socket */
1351 if (*clamd_options != '/') {
8e669ac1 1352
8544e77a
PP
1353 /* Confirmed in ClamAV source (0.95.3) that the TCPAddr option of clamd
1354 * only supports AF_INET, but we should probably be looking to the
1355 * future and rewriting this to be protocol-independent anyway. */
1356
8523533c
TK
1357 /* extract host and port part */
1358 if( sscanf(CS clamd_options, "%s %u", hostname, &port) != 2 ) {
1359 log_write(0, LOG_MAIN|LOG_PANIC,
1360 "malware acl condition: clamd: invalid socket '%s'", clamd_options);
1361 return DEFER;
1362 };
8e669ac1 1363
8523533c
TK
1364 /* Lookup the host */
1365 if((he = gethostbyname(CS hostname)) == 0) {
1366 log_write(0, LOG_MAIN|LOG_PANIC,
1367 "malware acl condition: clamd: failed to lookup host '%s'", hostname);
1368 return DEFER;
1369 }
8e669ac1 1370
8523533c 1371 in = *(struct in_addr *) he->h_addr_list[0];
8e669ac1 1372
8523533c
TK
1373 /* Open the ClamAV Socket */
1374 if ( (sock = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1375 log_write(0, LOG_MAIN|LOG_PANIC,
1376 "malware acl condition: clamd: unable to acquire socket (%s)",
1377 strerror(errno));
1378 return DEFER;
1379 }
8e669ac1 1380
8523533c 1381 if (ip_connect(sock, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
f1e894f3 1382 (void)close(sock);
8523533c
TK
1383 log_write(0, LOG_MAIN|LOG_PANIC,
1384 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1385 inet_ntoa(in), port, strerror(errno));
1386 return DEFER;
1387 }
2376f2f5 1388
8544e77a
PP
1389 } else {
1390 /* open the local socket */
1391 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
1392 log_write(0, LOG_MAIN|LOG_PANIC,
1393 "malware acl condition: clamd: unable to acquire socket (%s)",
1394 strerror(errno));
1395 return DEFER;
1396 }
2376f2f5 1397
8544e77a
PP
1398 server.sun_family = AF_UNIX;
1399 Ustrcpy(server.sun_path, clamd_options);
8e669ac1 1400
8544e77a
PP
1401 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
1402 (void)close(sock);
1403 log_write(0, LOG_MAIN|LOG_PANIC,
1404 "malware acl condition: clamd: unable to connect to UNIX socket %s (%s)",
1405 clamd_options, strerror(errno) );
1406 return DEFER;
1407 }
1408 }
8e669ac1 1409
8544e77a
PP
1410 /* have socket in variable "sock"; command to use is semi-independent of
1411 * the socket protocol. We use SCAN if is local (either Unix/local
1412 * domain socket, or explicitly told local) else we stream the data.
1413 * How we stream the data depends upon how we were built. */
2376f2f5 1414
8544e77a 1415 if (!use_scan_command) {
2376f2f5 1416
8544e77a
PP
1417#ifdef WITH_OLD_CLAMAV_STREAM
1418 /* "STREAM\n" command, get back a "PORT <N>\n" response, send data to
1419 * that port on a second connection; then in the scan-method-neutral
1420 * part, read the response back on the original connection. */
2376f2f5 1421
8544e77a
PP
1422 DEBUG(D_acl) debug_printf("Malware scan: issuing %s old-style remote scan (PORT)\n",
1423 scanner_name);
8e669ac1 1424
8544e77a
PP
1425 /* Pass the string to ClamAV (7 = "STREAM\n") */
1426 if (send(sock, "STREAM\n", 7, 0) < 0) {
1427 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1428 strerror(errno));
1429 (void)close(sock);
1430 return DEFER;
1431 }
1432 memset(av_buffer2, 0, sizeof(av_buffer2));
1433 bread = ip_recv(sock, av_buffer2, sizeof(av_buffer2), MALWARE_TIMEOUT);
8e669ac1 1434
8544e77a
PP
1435 if (bread < 0) {
1436 log_write(0, LOG_MAIN|LOG_PANIC,
1437 "malware acl condition: clamd: unable to read PORT from socket (%s)",
1438 strerror(errno));
1439 (void)close(sock);
1440 return DEFER;
1441 }
8e669ac1 1442
8544e77a
PP
1443 if (bread == sizeof(av_buffer)) {
1444 log_write(0, LOG_MAIN|LOG_PANIC,
1445 "malware acl condition: clamd: buffer too small");
1446 (void)close(sock);
1447 return DEFER;
1448 }
8e669ac1 1449
8544e77a
PP
1450 if (!(*av_buffer2)) {
1451 log_write(0, LOG_MAIN|LOG_PANIC,
1452 "malware acl condition: clamd: ClamAV returned null");
1453 (void)close(sock);
1454 return DEFER;
1455 }
8e669ac1 1456
8544e77a
PP
1457 av_buffer2[bread] = '\0';
1458 if( sscanf(CS av_buffer2, "PORT %u\n", &port) != 1 ) {
1459 log_write(0, LOG_MAIN|LOG_PANIC,
1460 "malware acl condition: clamd: Expected port information from clamd, got '%s'", av_buffer2);
1461 (void)close(sock);
1462 return DEFER;
1463 };
2376f2f5 1464
8544e77a
PP
1465 if ( (sockData = ip_socket(SOCK_STREAM, AF_INET)) < 0) {
1466 log_write(0, LOG_MAIN|LOG_PANIC,
1467 "malware acl condition: clamd: unable to acquire socket (%s)",
1468 strerror(errno));
1469 (void)close(sock);
1470 return DEFER;
1471 }
384152a6 1472
8544e77a
PP
1473 if (ip_connect(sockData, AF_INET, (uschar*)inet_ntoa(in), port, 5) < 0) {
1474 log_write(0, LOG_MAIN|LOG_PANIC,
1475 "malware acl condition: clamd: connection to %s, port %u failed (%s)",
1476 inet_ntoa(in), port, strerror(errno));
1477 (void)close(sockData); (void)close(sock);
1478 return DEFER;
1479 }
384152a6 1480
8544e77a
PP
1481#define CLOSE_SOCKDATA (void)close(sockData)
1482#else /* WITH_OLD_CLAMAV_STREAM not defined */
1483 /* New protocol: "zINSTREAM\n" followed by a sequence of <length><data>
1484 chunks, <n> a 4-byte number (network order), terminated by a zero-length
1485 chunk. */
384152a6 1486
8544e77a
PP
1487 DEBUG(D_acl) debug_printf("Malware scan: issuing %s new-style remote scan (zINSTREAM)\n",
1488 scanner_name);
384152a6 1489
8544e77a
PP
1490 /* Pass the string to ClamAV (10 = "zINSTREAM\0") */
1491 if (send(sock, "zINSTREAM", 10, 0) < 0) {
8523533c 1492 log_write(0, LOG_MAIN|LOG_PANIC,
8544e77a
PP
1493 "malware acl condition: clamd: unable to send zINSTREAM to socket (%s)",
1494 strerror(errno));
1495 (void)close(sock);
8523533c
TK
1496 return DEFER;
1497 }
8e669ac1 1498
8544e77a
PP
1499#define CLOSE_SOCKDATA /**/
1500#endif
8e669ac1 1501
8544e77a
PP
1502 /* calc file size */
1503 clam_fd = open(CS eml_filename, O_RDONLY);
1504 if (clam_fd == -1) {
8523533c 1505 log_write(0, LOG_MAIN|LOG_PANIC,
8544e77a
PP
1506 "malware acl condition: clamd: can't open spool file %s: %s",
1507 eml_filename, strerror(errno));
1508 CLOSE_SOCKDATA; (void)close(sock);
8523533c
TK
1509 return DEFER;
1510 }
8544e77a
PP
1511 fsize = lseek(clam_fd, 0, SEEK_END);
1512 if (fsize == -1) {
1513 log_write(0, LOG_MAIN|LOG_PANIC,
1514 "malware acl condition: clamd: can't seek spool file %s: %s",
1515 eml_filename, strerror(errno));
1516 CLOSE_SOCKDATA; (void)close(sock);
1517 return DEFER;
1518 }
1519 lseek(clam_fd, 0, SEEK_SET);
8e669ac1 1520
8544e77a
PP
1521 clamav_fbuf = (uschar *) malloc (fsize);
1522 if (!clamav_fbuf) {
1523 log_write(0, LOG_MAIN|LOG_PANIC,
1524 "malware acl condition: clamd: unable to allocate memory %u for file (%s)",
1525 fsize, eml_filename);
1526 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1527 return DEFER;
1528 }
8e669ac1 1529
8544e77a
PP
1530 result = read (clam_fd, clamav_fbuf, fsize);
1531 if (result == -1) {
1532 log_write(0, LOG_MAIN|LOG_PANIC,
1533 "malware acl condition: clamd: can't read spool file %s: %s",
1534 eml_filename, strerror(errno));
1535 CLOSE_SOCKDATA; (void)close(sock); (void)close(clam_fd);
1536 free(clamav_fbuf);
1537 return DEFER;
1538 }
1539 (void)close(clam_fd);
8e669ac1 1540
8544e77a
PP
1541 /* send file body to socket */
1542#ifdef WITH_OLD_CLAMAV_STREAM
1543 if (send(sockData, clamav_fbuf, fsize, 0) < 0) {
1544 log_write(0, LOG_MAIN|LOG_PANIC,
1545 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1546 CLOSE_SOCKDATA; (void)close(sock);
1547 free(clamav_fbuf);
1548 return DEFER;
1549 }
1550#else
1551 send_size = htonl(fsize);
1552 send_final_zeroblock = 0;
1553 if ((send(sock, &send_size, sizeof(send_size), 0) < 0) ||
1554 (send(sock, clamav_fbuf, fsize, 0) < 0) ||
1555 (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0))
1556 {
1557 log_write(0, LOG_MAIN|LOG_PANIC,
1558 "malware acl condition: clamd: unable to send file body to socket (%s:%u)", hostname, port);
1559 (void)close(sock);
1560 free(clamav_fbuf);
1561 return DEFER;
1562 }
1563#endif
8e669ac1 1564
8544e77a
PP
1565 free(clamav_fbuf);
1566
1567 CLOSE_SOCKDATA;
1568#undef CLOSE_SOCKDATA
1569
1570 } else { /* use scan command */
1571 /* Send a SCAN command pointing to a filename; then in the then in the
1572 * scan-method-neutral part, read the response back */
1573
1574/* ================================================================= */
1575
1576 /* Prior to the reworking post-Exim-4.72, this scanned a directory,
1577 which dates to when ClamAV needed us to break apart the email into the
1578 MIME parts (eg, with the now deprecated demime condition coming first).
1579 Some time back, ClamAV gained the ability to deconstruct the emails, so
1580 doing this would actually have resulted in the mail attachments being
1581 scanned twice, in the broken out files and from the original .eml.
1582 Since ClamAV now handles emails (and has for quite some time) we can
1583 just use the email file itself. */
1584 /* Pass the string to ClamAV (7 = "SCAN \n" + \0) */
1585 fits = string_format(file_name, sizeof(file_name), "SCAN %s\n",
1586 eml_filename);
1587 if (!fits) {
1588 (void)close(sock);
1589 log_write(0, LOG_MAIN|LOG_PANIC,
1590 "malware filename does not fit in buffer [malware_internal() clamd]");
1591 }
8e669ac1 1592
8544e77a
PP
1593 DEBUG(D_acl) debug_printf("Malware scan: issuing %s local-path scan [%s]\n",
1594 scanner_name, clamd_options);
8e669ac1 1595
8544e77a
PP
1596 if (send(sock, file_name, Ustrlen(file_name), 0) < 0) {
1597 (void)close(sock);
1598 log_write(0, LOG_MAIN|LOG_PANIC,"malware acl condition: clamd: unable to write to socket (%s)",
1599 strerror(errno));
1600 return DEFER;
1601 }
8e669ac1 1602
8544e77a
PP
1603 /* Do not shut down the socket for writing; a user report noted that
1604 * clamd 0.70 does not react well to this. */
1605 }
1606 /* Commands have been sent, no matter which scan method or connection
1607 * type we're using; now just read the result, independent of method. */
8e669ac1 1608
8523533c
TK
1609 /* Read the result */
1610 memset(av_buffer, 0, sizeof(av_buffer));
a9ccd69a 1611 bread = ip_recv(sock, av_buffer, sizeof(av_buffer), MALWARE_TIMEOUT);
f1e894f3 1612 (void)close(sock);
8523533c
TK
1613
1614 if (!(bread > 0)) {
1615 log_write(0, LOG_MAIN|LOG_PANIC,
1616 "malware acl condition: clamd: unable to read from socket (%s)",
1617 strerror(errno));
1618 return DEFER;
1619 }
8e669ac1 1620
8523533c
TK
1621 if (bread == sizeof(av_buffer)) {
1622 log_write(0, LOG_MAIN|LOG_PANIC,
1623 "malware acl condition: clamd: buffer too small");
1624 return DEFER;
1625 }
8e669ac1 1626
94a18f28
PP
1627 /* Check the result. ClamAV returns one of two result formats.
1628 In the basic mode, the response is of the form:
1629 infected: -> "<filename>: <virusname> FOUND"
1630 not-infected: -> "<filename>: OK"
1631 error: -> "<filename>: <errcode> ERROR
1632 If the ExtendedDetectionInfo option has been turned on, then we get:
1633 "<filename>: <virusname>(<virushash>:<virussize>) FOUND"
1634 for the infected case. Compare:
1635/tmp/eicar.com: Eicar-Test-Signature FOUND
1636/tmp/eicar.com: Eicar-Test-Signature(44d88612fea8a8f36de82e1278abb02f:68) FOUND
1637
1638 In the streaming case, clamd uses the filename "stream" which you should
1639 be able to verify with { ktrace clamdscan --stream /tmp/eicar.com }. (The
1640 client app will replace "stream" with the original filename before returning
1641 results to stdout, but the trace shows the data).
1642
1643 We will assume that the pathname passed to clamd from Exim does not contain
1644 a colon. We will have whined loudly above if the eml_filename does (and we're
1645 passing a filename to clamd). */
8e669ac1 1646
8523533c
TK
1647 if (!(*av_buffer)) {
1648 log_write(0, LOG_MAIN|LOG_PANIC,
1649 "malware acl condition: clamd: ClamAV returned null");
1650 return DEFER;
1651 }
8e669ac1 1652
94a18f28
PP
1653 /* strip newline at the end (won't be present for zINSTREAM)
1654 (also any trailing whitespace, which shouldn't exist, but we depend upon
1655 this below, so double-check) */
3c5cd616 1656 p = av_buffer + Ustrlen(av_buffer) - 1;
94a18f28 1657 if (*p == '\n') *p = '\0';
3c5cd616 1658
8544e77a
PP
1659 DEBUG(D_acl) debug_printf("Malware response: %s\n", av_buffer);
1660
94a18f28
PP
1661 while (isspace(*--p) && (p > av_buffer))
1662 *p = '\0';
1663 if (*p) ++p;
1664 response_end = p;
1665
8523533c 1666 /* colon in returned output? */
94a18f28 1667 if((p = Ustrchr(av_buffer,':')) == NULL) {
8523533c 1668 log_write(0, LOG_MAIN|LOG_PANIC,
94a18f28 1669 "malware acl condition: clamd: ClamAV returned malformed result (missing colon): %s",
8e669ac1 1670 av_buffer);
8523533c
TK
1671 return DEFER;
1672 }
8e669ac1 1673
3c5cd616 1674 /* strip filename */
94a18f28 1675 while (*p && isspace(*++p)) /**/;
8523533c 1676 vname = p;
94a18f28
PP
1677
1678 /* It would be bad to encounter a virus with "FOUND" in part of the name,
1679 but we should at least be resistant to it. */
1680 p = Ustrrchr(vname, ' ');
1681 if (p)
1682 result_tag = p + 1;
1683 else
1684 result_tag = vname;
1685
1686 if (Ustrcmp(result_tag, "FOUND") == 0) {
1687 /* p should still be the whitespace before the result_tag */
1688 while (isspace(*p)) --p;
1689 *++p = '\0';
1690 /* Strip off the extended information too, which will be in parens
1691 after the virus name, with no intervening whitespace. */
1692 if (*--p == ')') {
1693 /* "(hash:size)", so previous '(' will do; if not found, we have
1694 a curious virus name, but not an error. */
1695 p = Ustrrchr(vname, '(');
1696 if (p)
1697 *p = '\0';
1698 }
1699 Ustrncpy(malware_name_buffer, vname, sizeof(malware_name_buffer)-1);
1700 malware_name = malware_name_buffer;
1701 DEBUG(D_acl) debug_printf("Malware found, name \"%s\"\n", malware_name);
1702
1703 } else if (Ustrcmp(result_tag, "ERROR") == 0) {
1704 log_write(0, LOG_MAIN|LOG_PANIC,
1705 "malware acl condition: clamd: ClamAV returned: %s",
1706 av_buffer);
1707 return DEFER;
1708
1709 } else if (Ustrcmp(result_tag, "OK") == 0) {
1710 /* Everything should be OK */
1711 malware_name = NULL;
1712 DEBUG(D_acl) debug_printf("Malware not found\n");
1713
1714 } else {
1715 log_write(0, LOG_MAIN|LOG_PANIC,
1716 "malware acl condition: clamd: unparseable response from ClamAV: {%s}",
1717 av_buffer);
1718 return DEFER;
8523533c 1719 }
94a18f28
PP
1720
1721 } /* clamd */
1722
8523533c 1723 /* ----------------------------------------------------------------------- */
8e669ac1
PH
1724
1725
8523533c
TK
1726 /* "mksd" scanner type --------------------------------------------------- */
1727 else if (strcmpic(scanner_name,US"mksd") == 0) {
1728 uschar *mksd_options;
1729 char *mksd_options_end;
1730 uschar mksd_options_buffer[32];
1731 int mksd_maxproc = 1; /* default, if no option supplied */
1732 struct sockaddr_un server;
1733 int sock;
1734 int retval;
8e669ac1 1735
8523533c
TK
1736 if ((mksd_options = string_nextinlist(&av_scanner_work, &sep,
1737 mksd_options_buffer,
1738 sizeof(mksd_options_buffer))) != NULL) {
1739 mksd_maxproc = (int) strtol(CS mksd_options, &mksd_options_end, 10);
384152a6
TK
1740 if ((*mksd_options == '\0') || (*mksd_options_end != '\0') ||
1741 (mksd_maxproc < 1) || (mksd_maxproc > 32)) {
8523533c
TK
1742 log_write(0, LOG_MAIN|LOG_PANIC,
1743 "malware acl condition: mksd: invalid option '%s'", mksd_options);
1744 return DEFER;
1745 }
1746 }
8e669ac1 1747
8523533c
TK
1748 /* open the mksd socket */
1749 sock = socket(AF_UNIX, SOCK_STREAM, 0);
1750 if (sock < 0) {
1751 log_write(0, LOG_MAIN|LOG_PANIC,
1752 "malware acl condition: can't open UNIX socket.");
8e669ac1 1753 return DEFER;
8523533c
TK
1754 }
1755 server.sun_family = AF_UNIX;
1756 Ustrcpy(server.sun_path, "/var/run/mksd/socket");
1757 if (connect(sock, (struct sockaddr *) &server, sizeof(struct sockaddr_un)) < 0) {
f1e894f3 1758 (void)close(sock);
8523533c
TK
1759 log_write(0, LOG_MAIN|LOG_PANIC,
1760 "malware acl condition: unable to connect to mksd UNIX socket (/var/run/mksd/socket). errno=%d", errno);
1761 return DEFER;
1762 }
8e669ac1 1763
8523533c 1764 malware_name = NULL;
8e669ac1 1765
8544e77a
PP
1766 DEBUG(D_acl) debug_printf("Malware scan: issuing %s scan\n", scanner_name);
1767
1768 retval = mksd_scan_packed(sock, eml_filename);
8e669ac1 1769
8523533c
TK
1770 if (retval != OK)
1771 return retval;
1772 }
1773 /* ----------------------------------------------------------------------- */
1774
1775 /* "unknown" scanner type ------------------------------------------------- */
1776 else {
1777 log_write(0, LOG_MAIN|LOG_PANIC,
1778 "malware condition: unknown scanner type '%s'", scanner_name);
1779 return DEFER;
1780 };
1781 /* ----------------------------------------------------------------------- */
8e669ac1 1782
8523533c
TK
1783 /* set "been here, done that" marker */
1784 malware_ok = 1;
1785 };
1786
1787 /* match virus name against pattern (caseless ------->----------v) */
1788 if ( (malware_name != NULL) &&
1789 (regex_match_and_setup(re, malware_name, 0, -1)) ) {
8544e77a 1790 DEBUG(D_acl) debug_printf("Matched regex to malware [%s] [%s]\n", malware_regex, malware_name);
8523533c
TK
1791 return OK;
1792 }
1793 else {
1794 return FAIL;
1795 };
1796}
1797
1798
1799/* simple wrapper for reading lines from sockets */
1800int recv_line(int sock, uschar *buffer, int size) {
1801 uschar *p = buffer;
1802
1803 memset(buffer,0,size);
1804 /* read until \n */
1805 while(recv(sock,p,1,0) > -1) {
1806 if ((p-buffer) > (size-2)) break;
1807 if (*p == '\n') break;
1808 if (*p != '\r') p++;
1809 };
1810 *p = '\0';
1811
1812 return (p-buffer);
1813}
1814
1815
1816/* ============= private routines for the "mksd" scanner type ============== */
1817
1818#include <sys/uio.h>
1819
8544e77a 1820static int mksd_writev (int sock, struct iovec *iov, int iovcnt)
8523533c
TK
1821{
1822 int i;
8e669ac1 1823
8523533c
TK
1824 for (;;) {
1825 do
1826 i = writev (sock, iov, iovcnt);
1827 while ((i < 0) && (errno == EINTR));
1828 if (i <= 0) {
1829 close (sock);
1830 log_write(0, LOG_MAIN|LOG_PANIC,
1831 "malware acl condition: unable to write to mksd UNIX socket (/var/run/mksd/socket)");
1832 return -1;
1833 }
8e669ac1 1834
8523533c
TK
1835 for (;;)
1836 if (i >= iov->iov_len) {
1837 if (--iovcnt == 0)
1838 return 0;
1839 i -= iov->iov_len;
1840 iov++;
1841 } else {
1842 iov->iov_len -= i;
1843 iov->iov_base = CS iov->iov_base + i;
1844 break;
1845 }
1846 }
1847}
1848
8544e77a 1849static int mksd_read_lines (int sock, uschar *av_buffer, int av_buffer_size)
8523533c
TK
1850{
1851 int offset = 0;
1852 int i;
8e669ac1 1853
8523533c
TK
1854 do {
1855 if ((i = recv (sock, av_buffer+offset, av_buffer_size-offset, 0)) <= 0) {
1856 close (sock);
1857 log_write(0, LOG_MAIN|LOG_PANIC,
1858 "malware acl condition: unable to read from mksd UNIX socket (/var/run/mksd/socket)");
1859 return -1;
1860 }
8e669ac1 1861
8523533c
TK
1862 offset += i;
1863 /* offset == av_buffer_size -> buffer full */
1864 if (offset == av_buffer_size) {
1865 close (sock);
1866 log_write(0, LOG_MAIN|LOG_PANIC,
1867 "malware acl condition: malformed reply received from mksd");
1868 return -1;
1869 }
1870 } while (av_buffer[offset-1] != '\n');
8e669ac1 1871
8523533c
TK
1872 av_buffer[offset] = '\0';
1873 return offset;
1874}
1875
8544e77a 1876static int mksd_parse_line (char *line)
8523533c
TK
1877{
1878 char *p;
8e669ac1 1879
8523533c
TK
1880 switch (*line) {
1881 case 'O':
1882 /* OK */
1883 return OK;
1884 case 'E':
1885 case 'A':
1886 /* ERR */
1887 if ((p = strchr (line, '\n')) != NULL)
1888 (*p) = '\0';
1889 log_write(0, LOG_MAIN|LOG_PANIC,
1890 "malware acl condition: mksd scanner failed: %s", line);
1891 return DEFER;
1892 default:
1893 /* VIR */
1894 if ((p = strchr (line, '\n')) != NULL) {
1895 (*p) = '\0';
1896 if (((p-line) > 5) && ((p-line) < sizeof (malware_name_buffer)) && (line[3] == ' '))
1897 if (((p = strchr (line+4, ' ')) != NULL) && ((p-line) > 4)) {
1898 (*p) = '\0';
1899 Ustrcpy (malware_name_buffer, line+4);
384152a6 1900 malware_name = malware_name_buffer;
8523533c
TK
1901 return OK;
1902 }
1903 }
1904 log_write(0, LOG_MAIN|LOG_PANIC,
1905 "malware acl condition: malformed reply received from mksd: %s", line);
1906 return DEFER;
1907 }
1908}
1909
8544e77a 1910static int mksd_scan_packed(int sock, uschar *scan_filename)
8523533c 1911{
8544e77a 1912 struct iovec iov[3];
1ba28e2b 1913 const char *cmd = "MSQ\n";
8523533c 1914 uschar av_buffer[1024];
8e669ac1 1915
1ba28e2b 1916 iov[0].iov_base = (void *) cmd;
8523533c 1917 iov[0].iov_len = 3;
8544e77a
PP
1918 iov[1].iov_base = CS scan_filename;
1919 iov[1].iov_len = Ustrlen(scan_filename);
1ba28e2b 1920 iov[2].iov_base = (void *) cmd + 3;
8544e77a
PP
1921 iov[2].iov_len = 1;
1922
1923 if (mksd_writev (sock, iov, 3) < 0)
8523533c 1924 return DEFER;
8e669ac1 1925
8523533c
TK
1926 if (mksd_read_lines (sock, av_buffer, sizeof (av_buffer)) < 0)
1927 return DEFER;
8e669ac1 1928
8523533c 1929 close (sock);
8e669ac1 1930
8523533c
TK
1931 return mksd_parse_line (CS av_buffer);
1932}
1933
1934#endif