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