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