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