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