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