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