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