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