diff options
author | Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de> | 2018-03-31 00:06:47 +0200 |
---|---|---|
committer | Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de> | 2018-04-02 22:00:36 +0200 |
commit | ad93c40fe70f7de49ffb8601a589e9ffa117d512 (patch) | |
tree | cd5e3a72a219b00aef74c13ee6c7f277265e09f1 /src | |
parent | 295bebdad432a6930224f21f879c98eb447354c3 (diff) |
Avast: improve compliance with avast-protocol(5)
Treat scanner errors as malware. Defer on scanner tmpfail
only.
Diffstat (limited to 'src')
-rw-r--r-- | src/src/malware.c | 337 |
1 files changed, 210 insertions, 127 deletions
diff --git a/src/src/malware.c b/src/src/malware.c index d24f09b88..7c57c0090 100644 --- a/src/src/malware.c +++ b/src/src/malware.c @@ -137,9 +137,11 @@ static const pcre * kav_re_inf = NULL; #ifndef DISABLE_MAL_AVAST static const uschar * ava_re_clean_str = US "(?!\\\\)\\t\\[\\+\\]"; -static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d\\.\\d\\t\\d\\s(.*)"; +static const uschar * ava_re_virus_str = US "(?!\\\\)\\t\\[L\\]\\d+\\.0\\t0\\s(.*)"; +static const uschar * ava_re_error_str = US "(?!\\\\)\\t\\[E\\]\\d+\\.0\\tError\\s\\d+\\s(.*)"; static const pcre * ava_re_clean = NULL; static const pcre * ava_re_virus = NULL; +static const pcre * ava_re_error = NULL; #endif #ifndef DISABLE_MAL_FFROT6D @@ -176,27 +178,58 @@ extern int spool_mbox_ok; extern uschar spooled_message_id[MESSAGE_ID_LENGTH+1]; +/* Some (currently avast only) use backslash escaped whitespace, +this function undoes these escapes */ +static inline void +unescape(char *p) { + uschar *p0; + for (; *p; ++p) + if (*p == '\\' && (isspace(p[1]) || p[1] == '\\')) + for (p0 = p; *p0; ++p0) *p0 = p0[1]; +} +/* --- malware_*_defer --- */ static inline int -malware_errlog_defer(const uschar * str) +malware_panic_defer(const uschar * str) { log_write(0, LOG_MAIN|LOG_PANIC, "malware acl condition: %s", str); return DEFER; } - -static int -m_errlog_defer(struct scan * scanent, const uschar * hostport, +static inline int +malware_log_defer(const uschar * str) +{ +log_write(0, LOG_MAIN, "malware acl condition: %s", str); +return DEFER; +} +/* --- m_*_defer --- */ +static inline int +m_panic_defer(struct scan * scanent, const uschar * hostport, const uschar * str) { -return malware_errlog_defer(string_sprintf("%s %s : %s", +return malware_panic_defer(string_sprintf("%s %s : %s", scanent->name, hostport ? hostport : CUS"", str)); } -static int -m_errlog_defer_3(struct scan * scanent, const uschar * hostport, +static inline int +m_log_defer(struct scan * scanent, const uschar * hostport, + const uschar * str) +{ +return malware_log_defer(string_sprintf("%s %s : %s", + scanent->name, hostport ? hostport : CUS"", str)); +} +/* --- m_*_defer_3 */ +static inline int +m_panic_defer_3(struct scan * scanent, const uschar * hostport, + const uschar * str, int fd_to_close) +{ +(void) close(fd_to_close); +return m_panic_defer(scanent, hostport, str); +} +static inline int +m_log_defer_3(struct scan * scanent, const uschar * hostport, const uschar * str, int fd_to_close) { (void) close(fd_to_close); -return m_errlog_defer(scanent, hostport, str); +return m_log_defer(scanent, hostport, str); } /*************************************************/ @@ -341,7 +374,7 @@ for (;;) while (i < 0 && errno == EINTR); if (i <= 0) { - (void) malware_errlog_defer( + (void) malware_panic_defer( US"unable to write to mksd UNIX socket (/var/run/mksd/socket)"); return -1; } @@ -373,7 +406,7 @@ do i = ip_recv(sock, av_buffer+offset, av_buffer_size-offset, tmo-time(NULL)); if (i <= 0) { - (void) malware_errlog_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); + (void) malware_panic_defer(US"unable to read from mksd UNIX socket (/var/run/mksd/socket)"); return -1; } @@ -381,7 +414,7 @@ do /* offset == av_buffer_size -> buffer full */ if (offset == av_buffer_size) { - (void) malware_errlog_defer(US"malformed reply received from mksd"); + (void) malware_panic_defer(US"malformed reply received from mksd"); return -1; } } while (av_buffer[offset-1] != '\n'); @@ -404,7 +437,7 @@ switch (*line) case 'A': /* ERR */ if ((p = strchr (line, '\n')) != NULL) *p = '\0'; - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, string_sprintf("scanner failed: %s", line)); default: /* VIR */ @@ -422,7 +455,7 @@ switch (*line) return OK; } } - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, string_sprintf("malformed reply received: %s", line)); } } @@ -515,7 +548,7 @@ if (!malware_re) /* Ensure the eml mbox file is spooled up */ if (!(mbox_file = spool_mbox(&mbox_size, scan_filename, &eml_filename))) - return malware_errlog_defer(US"error while creating mbox spool file"); + return malware_panic_defer(US"error while creating mbox spool file"); /* None of our current scanners need the mbox file as a stream (they use the name), so we can close it right away. Get the directory too. */ @@ -535,20 +568,20 @@ if ( strcmpic(malware_re,US"true") == 0 { if ( !malware_default_re && !(malware_default_re = m_pcre_compile(malware_regex_default, &errstr))) - return malware_errlog_defer(errstr); + return malware_panic_defer(errstr); malware_re = malware_regex_default; re = malware_default_re; } /* compile the regex, see if it works */ else if (!(re = m_pcre_compile(malware_re, &errstr))) - return malware_errlog_defer(errstr); + return malware_panic_defer(errstr); /* if av_scanner starts with a dollar, expand it first */ if (*av_scanner == '$') { if (!(av_scanner_work = expand_string(av_scanner))) - return malware_errlog_defer( + return malware_panic_defer( string_sprintf("av_scanner starts with $, but expansion failed: %s", expand_string_message)); @@ -564,14 +597,14 @@ if (!malware_ok) { /* find the scanner type from the av_scanner option */ if (!(scanner_name = string_nextinlist(&av_scanner_work, &sep, NULL, 0))) - return malware_errlog_defer(US"av_scanner configuration variable is empty"); + return malware_panic_defer(US"av_scanner configuration variable is empty"); if (!timeout) timeout = MALWARE_TIMEOUT; tmo = time(NULL) + timeout; for (scanent = m_scans; ; scanent++) { if (!scanent->name) - return malware_errlog_defer(string_sprintf("unknown scanner type '%s'", + return malware_panic_defer(string_sprintf("unknown scanner type '%s'", scanner_name)); if (strcmpic(scanner_name, US scanent->name) != 0) continue; @@ -592,7 +625,7 @@ if (!malware_ok) default: /* compiler quietening */ break; } if (sock < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); break; } @@ -623,7 +656,7 @@ if (!malware_ok) /* send scan request */ if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); while ((len = recv_line(sock, buf, sizeof(buf), tmo)) >= 0) if (len > 0) @@ -665,24 +698,24 @@ if (!malware_ok) if ((!fprot6d_re_virus && !(fprot6d_re_virus = m_pcre_compile(fprot6d_re_virus_str, &errstr))) || (!fprot6d_re_error && !(fprot6d_re_error = m_pcre_compile(fprot6d_re_error_str, &errstr)))) - return malware_errlog_defer(errstr); + return malware_panic_defer(errstr); scanrequest = string_sprintf("SCAN FILE %s\n", eml_filename); DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s: %s\n", scanner_name, scanrequest); if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest), &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); if (bread <= 0) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read from socket (%s)", strerror(errno)), sock); if (bread == sizeof(av_buffer)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"buffer too small", sock); av_buffer[bread] = '\0'; @@ -691,7 +724,7 @@ if (!malware_ok) m_sock_send(sock, US"QUIT\n", 5, 0); if ((e = m_pcre_exec(fprot6d_re_error, linebuffer))) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("scanner reported error (%s)", e), sock); if (!(malware_name = m_pcre_exec(fprot6d_re_virus, linebuffer))) @@ -721,7 +754,7 @@ if (!malware_ok) { /* calc file size */ if ((drweb_fd = open(CCS eml_filename, O_RDONLY)) == -1) - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("can't open spool file %s: %s", eml_filename, strerror(errno)), sock); @@ -731,7 +764,7 @@ if (!malware_ok) int err; badseek: err = errno; (void)close(drweb_fd); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("can't seek spool file %s: %s", eml_filename, strerror(err)), sock); @@ -740,7 +773,7 @@ badseek: err = errno; if ((off_t)fsize_uint != fsize) { (void)close(drweb_fd); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("seeking spool file %s, size overflow", eml_filename), sock); @@ -759,7 +792,7 @@ badseek: err = errno; (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0)) { (void)close(drweb_fd); - return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send commands to socket (%s)", scanner_options), sock); } @@ -767,7 +800,7 @@ badseek: err = errno; if (!(drweb_fbuf = US malloc(fsize_uint))) { (void)close(drweb_fd); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("unable to allocate memory %u for file (%s)", fsize_uint, eml_filename), sock); @@ -778,7 +811,7 @@ badseek: err = errno; int err = errno; (void)close(drweb_fd); free(drweb_fbuf); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), sock); @@ -789,7 +822,7 @@ badseek: err = errno; if (send(sock, drweb_fbuf, fsize, 0) < 0) { free(drweb_fbuf); - return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send file body to socket (%s)", scanner_options), sock); } @@ -807,19 +840,19 @@ badseek: err = errno; (send(sock, &drweb_slen, sizeof(drweb_slen), 0) < 0) || (send(sock, eml_filename, Ustrlen(eml_filename), 0) < 0) || (send(sock, &drweb_fin, sizeof(drweb_fin), 0) < 0)) - return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send commands to socket (%s)", scanner_options), sock); } /* wait for result */ if (!recv_len(sock, &drweb_rc, sizeof(drweb_rc), tmo)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"unable to read return code", sock); drweb_rc = ntohl(drweb_rc); if (!recv_len(sock, &drweb_vnum, sizeof(drweb_vnum), tmo)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"unable to read the number of viruses", sock); drweb_vnum = ntohl(drweb_vnum); @@ -843,14 +876,14 @@ badseek: err = errno; /* read the size of report */ if (!recv_len(sock, &drweb_slen, sizeof(drweb_slen), tmo)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"cannot read report size", sock); drweb_slen = ntohl(drweb_slen); tmpbuf = store_get(drweb_slen); /* read report body */ if (!recv_len(sock, tmpbuf, drweb_slen, tmo)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"cannot read report string", sock); tmpbuf[drweb_slen] = '\0'; @@ -888,7 +921,7 @@ badseek: err = errno; * DERR_CRC_ERROR, DERR_READSOCKET, DERR_WRITE_ERR * and others are ignored */ if (drweb_s) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("drweb daemon retcode 0x%x (%s)", drweb_rc, drweb_s), sock); @@ -910,7 +943,7 @@ badseek: err = errno; recv_line(sock, buf, sizeof(buf), tmo); if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unavailable (Responded: %s).", ((buf[0] != 0) ? buf : US "nothing") ), sock); @@ -923,7 +956,7 @@ badseek: err = errno; DEBUG(D_acl) debug_printf_indent("Malware scan: issuing %s %s\n", scanner_name, buf); if (m_sock_send(sock, buf, Ustrlen(buf), &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); malware_name = NULL; result = 0; @@ -934,7 +967,7 @@ badseek: err = errno; break; if (buf[0] == '5') /* aveserver is having problems */ { - result = m_errlog_defer(scanent, CUS callout_address, + result = m_panic_defer(scanent, CUS callout_address, string_sprintf("unable to scan file %s (Responded: %s).", eml_filename, buf)); break; @@ -948,14 +981,14 @@ badseek: err = errno; } if (m_sock_send(sock, US"quit\r\n", 6, &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); /* read aveserver's greeting and see if it is ready (2xx greeting) */ buf[0] = 0; recv_line(sock, buf, sizeof(buf), tmo); if (buf[0] != '2') /* aveserver is having problems */ - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to quit dialogue (Responded: %s).", ((buf[0] != 0) ? buf : US "nothing") ), sock); @@ -990,12 +1023,12 @@ badseek: err = errno; { if (m_sock_send(sock, cmdopt[i], Ustrlen(cmdopt[i]), &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); if (bread > 0) av_buffer[bread]='\0'; if (bread < 0) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read answer %d (%s)", i, strerror(errno)), sock); for (j = 0; j < bread; j++) @@ -1007,7 +1040,7 @@ badseek: err = errno; file_name = string_sprintf("SCAN\t%s\n", eml_filename); if (m_sock_send(sock, file_name, Ustrlen(file_name), &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); /* set up match */ /* todo also SUSPICION\t */ @@ -1025,7 +1058,7 @@ badseek: err = errno; errno = ETIMEDOUT; i = av_buffer+sizeof(av_buffer)-p; if ((bread= ip_recv(sock, p, i-1, tmo-time(NULL))) < 0) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read result (%s)", strerror(errno)), sock); @@ -1085,11 +1118,11 @@ badseek: err = errno; /* send scan request */ if (m_sock_send(sock, scanrequest, Ustrlen(scanrequest)+1, &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); /* wait for result */ if (!recv_len(sock, tmpbuf, 2, tmo)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"unable to read 2 bytes from socket.", sock); /* get errorcode from one nibble */ @@ -1097,14 +1130,14 @@ badseek: err = errno; switch(kav_rc) { case 5: case 6: /* improper kavdaemon configuration */ - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"please reconfigure kavdaemon to NOT disinfect or remove infected files.", sock); case 1: - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"reported 'scanning not completed' (code 1).", sock); case 7: - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"reported 'kavdaemon damaged' (code 7).", sock); } @@ -1126,7 +1159,7 @@ badseek: err = errno; { /* read report size */ if (!recv_len(sock, &kav_reportlen, 4, tmo)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"cannot read report size", sock); /* it's possible that avp returns av_buffer[1] == 1 but the @@ -1187,19 +1220,19 @@ badseek: err = errno; uschar *p; if (!cmdline_scanner) - return m_errlog_defer(scanent, NULL, errstr); + return m_panic_defer(scanent, NULL, errstr); /* find scanner output trigger */ cmdline_trigger_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing trigger specification", &errstr); if (!cmdline_trigger_re) - return m_errlog_defer(scanent, NULL, errstr); + return m_panic_defer(scanent, NULL, errstr); /* find scanner name regex */ cmdline_regex_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing virus name regex specification", &errstr); if (!cmdline_regex_re) - return m_errlog_defer(scanent, NULL, errstr); + return m_panic_defer(scanent, NULL, errstr); /* prepare scanner call; despite the naming, file_name holds a directory name which is documented as the value given to %s. */ @@ -1224,7 +1257,7 @@ badseek: err = errno; { int err = errno; signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, string_sprintf("call (%s) failed: %s.", commandline, strerror(err))); } scanner_fd = fileno(scanner_out); @@ -1236,7 +1269,7 @@ badseek: err = errno; int err = errno; (void) pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, NULL, string_sprintf( + return m_panic_defer(scanent, NULL, string_sprintf( "opening scanner output file (%s) failed: %s.", file_name, strerror(err))); } @@ -1252,7 +1285,7 @@ badseek: err = errno; break; (void) pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, NULL, string_sprintf( + return m_panic_defer(scanent, NULL, string_sprintf( "unable to read from scanner (%s): %s", commandline, strerror(err))); } @@ -1262,7 +1295,7 @@ badseek: err = errno; /* short write */ (void) pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); - return m_errlog_defer(scanent, NULL, string_sprintf( + return m_panic_defer(scanent, NULL, string_sprintf( "short write on scanner output file (%s).", file_name)); } putc('\n', scanner_record); @@ -1277,7 +1310,7 @@ badseek: err = errno; sep = pclose(scanner_out); signal(SIGCHLD,eximsigchld); signal(SIGPIPE,eximsigpipe); if (sep != 0) - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, sep == -1 ? string_sprintf("running scanner failed: %s", strerror(sep)) : string_sprintf("scanner returned error code: %d", sep)); @@ -1323,14 +1356,14 @@ badseek: err = errno; if ( write(sock, file_name, Ustrlen(file_name)) < 0 || write(sock, "\n", 1) != 1 ) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to write to UNIX socket (%s)", scanner_options), sock); /* wait for result */ memset(av_buffer, 0, sizeof(av_buffer)); if ((bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL))) <= 0) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read from UNIX socket (%s)", scanner_options), sock); @@ -1342,7 +1375,7 @@ badseek: err = errno; malware_name = string_copy(&av_buffer[2]); } else if (!strncmp(CS av_buffer, "-1", 2)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"scanner reported error", sock); else /* all ok, no virus */ malware_name = NULL; @@ -1400,7 +1433,7 @@ badseek: err = errno; /* parse options */ if (clamd_option(cd, sublist, &subsep) != OK) - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, string_sprintf("bad option '%s'", scanner_options)); cv[0] = cd; } @@ -1432,13 +1465,13 @@ badseek: err = errno; sublist = scanner_options; if (!(cd->hostspec = string_nextinlist(&sublist, &subsep, NULL, 0))) { - (void) m_errlog_defer(scanent, NULL, + (void) m_panic_defer(scanent, NULL, string_sprintf("missing address: '%s'", scanner_options)); continue; } if (!(s = string_nextinlist(&sublist, &subsep, NULL, 0))) { - (void) m_errlog_defer(scanent, NULL, + (void) m_panic_defer(scanent, NULL, string_sprintf("missing port: '%s'", scanner_options)); continue; } @@ -1447,13 +1480,13 @@ badseek: err = errno; /* parse options */ /*XXX should these options be common over scanner types? */ if (clamd_option(cd, sublist, &subsep) != OK) - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, string_sprintf("bad option '%s'", scanner_options)); cv[num_servers++] = cd; if (num_servers >= MAX_CLAMD_SERVERS) { - (void) m_errlog_defer(scanent, NULL, + (void) m_panic_defer(scanent, NULL, US"More than " MAX_CLAMD_SERVERS_S " clamd servers " "specified; only using the first " MAX_CLAMD_SERVERS_S ); break; @@ -1463,14 +1496,14 @@ badseek: err = errno; /* check if we have at least one server */ if (!num_servers) - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, US"no useable server addresses in malware configuration option."); } /* See the discussion of response formats below to see why we really don't like colons in filenames when passing filenames to ClamAV. */ if (use_scan_command && Ustrchr(eml_filename, ':')) - return m_errlog_defer(scanent, NULL, + return m_panic_defer(scanent, NULL, string_sprintf("local/SCAN mode incompatible with" \ " : in path to email filename [%s]", eml_filename)); @@ -1516,7 +1549,7 @@ badseek: err = errno; if (sock >= 0) break; - (void) m_errlog_defer(scanent, CUS callout_address, errstr); + (void) m_panic_defer(scanent, CUS callout_address, errstr); /* Remove the server from the list. XXX We should free the memory */ num_servers--; @@ -1525,7 +1558,7 @@ badseek: err = errno; } if (num_servers == 0) - return m_errlog_defer(scanent, NULL, US"all servers failed"); + return m_panic_defer(scanent, NULL, US"all servers failed"); } else for (;;) @@ -1536,7 +1569,7 @@ badseek: err = errno; break; } if (cv[0]->retry <= 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); while (cv[0]->retry > 0) cv[0]->retry = sleep(cv[0]->retry); } @@ -1558,7 +1591,7 @@ badseek: err = errno; /* Pass the string to ClamAV (10 = "zINSTREAM\0"), if not already sent */ if (cmd_str.len) if (send(sock, cmd_str.data, cmd_str.len, 0) < 0) - return m_errlog_defer_3(scanent, CUS hostname, + return m_panic_defer_3(scanent, CUS hostname, string_sprintf("unable to send zINSTREAM to socket (%s)", strerror(errno)), sock); @@ -1567,7 +1600,7 @@ badseek: err = errno; if ((clam_fd = open(CS eml_filename, O_RDONLY)) < 0) { int err = errno; - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("can't open spool file %s: %s", eml_filename, strerror(err)), sock); @@ -1577,7 +1610,7 @@ badseek: err = errno; int err; b_seek: err = errno; (void)close(clam_fd); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("can't seek spool file %s: %s", eml_filename, strerror(err)), sock); @@ -1586,7 +1619,7 @@ b_seek: err = errno; if ((off_t)fsize_uint != fsize) { (void)close(clam_fd); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("seeking spool file %s, size overflow", eml_filename), sock); @@ -1597,7 +1630,7 @@ b_seek: err = errno; if (!(clamav_fbuf = US malloc(fsize_uint))) { (void)close(clam_fd); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("unable to allocate memory %u for file (%s)", fsize_uint, eml_filename), sock); @@ -1607,7 +1640,7 @@ b_seek: err = errno; { int err = errno; free(clamav_fbuf); (void)close(clam_fd); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("can't read spool file %s: %s", eml_filename, strerror(err)), sock); @@ -1622,7 +1655,7 @@ b_seek: err = errno; (send(sock, &send_final_zeroblock, sizeof(send_final_zeroblock), 0) < 0)) { free(clamav_fbuf); - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, string_sprintf("unable to send file body to socket (%s)", hostname), sock); } @@ -1652,7 +1685,7 @@ b_seek: err = errno; if (cmd_str.len) if (send(sock, cmd_str.data, cmd_str.len, 0) < 0) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to write to socket (%s)", strerror(errno)), sock); @@ -1669,12 +1702,12 @@ b_seek: err = errno; sock = -1; if (bread <= 0) - return m_errlog_defer(scanent, CUS callout_address, + return m_panic_defer(scanent, CUS callout_address, string_sprintf("unable to read from socket (%s)", errno == 0 ? "EOF" : strerror(errno))); if (bread == sizeof(av_buffer)) - return m_errlog_defer(scanent, CUS callout_address, + return m_panic_defer(scanent, CUS callout_address, US"buffer too small"); /* We're now assured of a NULL at the end of av_buffer */ @@ -1699,7 +1732,7 @@ b_seek: err = errno; passing a filename to clamd). */ if (!(*av_buffer)) - return m_errlog_defer(scanent, CUS callout_address, + return m_panic_defer(scanent, CUS callout_address, US"ClamAV returned null"); /* strip newline at the end (won't be present for zINSTREAM) @@ -1716,7 +1749,7 @@ b_seek: err = errno; /* colon in returned output? */ if(!(p = Ustrchr(av_buffer,':'))) - return m_errlog_defer(scanent, CUS callout_address, string_sprintf( + return m_panic_defer(scanent, CUS callout_address, string_sprintf( "ClamAV returned malformed result (missing colon): %s", av_buffer)); @@ -1749,7 +1782,7 @@ b_seek: err = errno; } else if (Ustrcmp(result_tag, "ERROR") == 0) - return m_errlog_defer(scanent, CUS callout_address, + return m_panic_defer(scanent, CUS callout_address, string_sprintf("ClamAV returned: %s", av_buffer)); else if (Ustrcmp(result_tag, "OK") == 0) @@ -1760,7 +1793,7 @@ b_seek: err = errno; } else - return m_errlog_defer(scanent, CUS callout_address, + return m_panic_defer(scanent, CUS callout_address, string_sprintf("unparseable response from ClamAV: {%s}", av_buffer)); break; @@ -1791,7 +1824,7 @@ b_seek: err = errno; uschar * s = Ustrchr(sockline_scanner, '%'); if (s++) if ((*s != 's' && *s != '%') || Ustrchr(s+1, '%')) - return m_errlog_defer_3(scanent, NULL, + return m_panic_defer_3(scanent, NULL, US"unsafe sock scanner call spec", sock); } else @@ -1803,13 +1836,13 @@ b_seek: err = errno; sockline_trig_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing trigger specification", &errstr); if (!sockline_trig_re) - return m_errlog_defer_3(scanent, NULL, errstr, sock); + return m_panic_defer_3(scanent, NULL, errstr, sock); /* find virus name regex */ sockline_name_re = m_pcre_nextinlist(&av_scanner_work, &sep, "missing virus name regex specification", &errstr); if (!sockline_name_re) - return m_errlog_defer_3(scanent, NULL, errstr, sock); + return m_panic_defer_3(scanent, NULL, errstr, sock); /* prepare scanner call - security depends on expansions check above */ commandline = string_sprintf( CS sockline_scanner, CS eml_filename); @@ -1818,18 +1851,18 @@ b_seek: err = errno; /* Pass the command string to the socket */ if (m_sock_send(sock, commandline, Ustrlen(commandline), &errstr) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); /* Read the result */ bread = ip_recv(sock, av_buffer, sizeof(av_buffer), tmo-time(NULL)); if (bread <= 0) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf("unable to read from socket (%s)", strerror(errno)), sock); if (bread == sizeof(av_buffer)) - return m_errlog_defer_3(scanent, CUS callout_address, + return m_panic_defer_3(scanent, CUS callout_address, US"buffer too small", sock); av_buffer[bread] = '\0'; linebuffer = string_copy(av_buffer); @@ -1865,12 +1898,12 @@ b_seek: err = errno; || mksd_maxproc < 1 || mksd_maxproc > 32 ) - return m_errlog_defer(scanent, CUS callout_address, + return m_panic_defer(scanent, CUS callout_address, string_sprintf("invalid option '%s'", scanner_options)); } if((sock = ip_unixsocket(US "/var/run/mksd/socket", &errstr)) < 0) - return m_errlog_defer(scanent, CUS callout_address, errstr); + return m_panic_defer(scanent, CUS callout_address, errstr); malware_name = NULL; @@ -1888,12 +1921,13 @@ b_seek: err = errno; #ifndef DISABLE_MAL_AVAST case M_AVAST: /* "avast" scanner type ----------------------------------- */ { - int ovector[1*3]; + int ovector[10*3]; uschar buf[1024]; uschar * scanrequest; enum {AVA_HELO, AVA_OPT, AVA_RSP, AVA_DONE} avast_stage; int nread; int more_data; + uschar * error_message = NULL; /* According to Martin Tuma @avast the protocol uses "escaped whitespace", that is, every embedded whitespace is backslash @@ -1905,13 +1939,44 @@ b_seek: err = errno; [E] - some error occured Such marker follows the first non-escaped TAB. For more information see avast-protocol(5) - */ + + We observed two cases: + -> SCAN /file + <- /file [E]0.0 Error 13 Permission denied + <- 451 SCAN Engine error 13 permission denied + + -> SCAN /file + <- /file… [E]3.0 Error 41120 The file is a decompression bomb + <- /file… [+]2.0 + <- /file… [+]2.0 0 Eicar Test Virus!!! + <- 200 SCAN OK + + If the scanner returns 4xx, DEFER is a good decision, combined + with a panic log entry, to get the admin's attention. + + If the scanner returns 200, we reject it as malware, if found any, + or, in case of an error, we set the malware message to the error + string. + + Some of the >= 42000 errors are message related - usually some + broken archives etc, but some of them are e.g. license related. + Once the license expires the engine starts returning errors for + every scanning attempt. I¹ have the full list of the error codes + but it is not a public API and is subject to change. It is hard + for me to say what you should do in case of an engine error. You + can have a “Treat * unscanned file as infection” policy or “Treat + unscanned file as clean” policy. ¹) Jakub Bednar + + */ + if ( ( !ava_re_clean && !(ava_re_clean = m_pcre_compile(ava_re_clean_str, &errstr))) || ( !ava_re_virus && !(ava_re_virus = m_pcre_compile(ava_re_virus_str, &errstr))) + || ( !ava_re_error + && !(ava_re_error = m_pcre_compile(ava_re_error_str, &errstr))) ) - return malware_errlog_defer(errstr); + return malware_panic_defer(errstr); /* wait for result */ for (avast_stage = AVA_HELO; @@ -1963,10 +2028,10 @@ b_seek: err = errno; /* send config-cmd or scan-request to socket */ len = Ustrlen(scanrequest); - if (send(sock, scanrequest, len, 0) < 0) + if (send(sock, scanrequest, len, 0) == -1) { scanrequest[len-1] = '\0'; - return m_errlog_defer_3(scanent, CUS callout_address, string_sprintf( + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send request '%s' to socket (%s): %s", scanrequest, scanner_options, strerror(errno)), sock); } @@ -1975,36 +2040,52 @@ b_seek: err = errno; case AVA_RSP: - if (Ustrncmp(buf, "200", 3) == 0) - { /* we're done finally */ - if (send(sock, "QUIT\n", 5, 0) < 0) /* courtesy */ - return m_errlog_defer_3(scanent, CUS callout_address, + if (isdigit(buf[0])) + { /* we're done, this is the last response line from the scanner */ + DEBUG(D_acl) debug_printf_indent("sent to avast QUIT\n"); + if (send(sock, "QUIT\n", 5, 0) == -1) /* courtesy */ + return m_panic_defer_3(scanent, CUS callout_address, string_sprintf( "unable to send quit request to socket (%s): %s", scanner_options, strerror(errno)), sock); + if (buf[0] != '2') error_message = buf; avast_stage = AVA_DONE; goto endloop; } - if (malware_name) break; /* found malware already, nothing to do anymore */ + #if 0 + /* found malware or another error already, nothing to do anymore, just read on */ + if (malware_name || error_message) + break; + #endif if (pcre_exec(ava_re_clean, NULL, CS buf, slen, 0, 0, ovector, nelem(ovector)) > 0) break; - if (malware_name = m_pcre_exec(ava_re_virus, buf)) - { /* remove backslash in front of [whitespace|backslash] */ - uschar * p, * p0; - for (p = malware_name; *p; ++p) - if (*p == '\\' && (isspace(p[1]) || p[1] == '\\')) - for (p0 = p; *p0; ++p0) *p0 = p0[1]; - - DEBUG(D_acl) - debug_printf_indent("unescaped malware name: '%s'\n", malware_name); - break; - } + if (malware_name) + break; + + if (malware_name = m_pcre_exec(ava_re_virus, buf)) + { + unescape(malware_name); + DEBUG(D_acl) + debug_printf_indent("unescaped malware name: '%s'\n", malware_name); + break; + } + + /*if (pcre_exec(ava_re_error, NULL, CS buf, nread, 0, 0, ovector, nelem(ovector)) == 2) + unescape(malware_name = buf + ovector[1*2]); + */ + if (malware_name = m_pcre_exec(ava_re_error, buf)) + { + unescape(malware_name); + DEBUG(D_acl) + debug_printf_indent("unescaped error message: '%s'\n", malware_name); + break; + } /* here also for any unexpected response from the scanner */ goto endloop; @@ -2020,14 +2101,14 @@ b_seek: err = errno; { case AVA_HELO: case AVA_OPT: - case AVA_RSP: return m_errlog_defer_3(scanent, CUS callout_address, - nread >= 0 - ? string_sprintf( - "invalid response from scanner: '%s'", buf) - : nread == -1 - ? US"EOF from scanner" - : US"timeout from scanner", - sock); + case AVA_RSP: + if (nread == -1) error_message = "EOF from scanner"; + else if (nread < 0) error_message = "timeout from scanner"; + + case AVA_DONE: + if (error_message) + return m_panic_defer_3(scanent, CUS callout_address, error_message, sock); + default: break; } break; @@ -2151,6 +2232,8 @@ if (!ava_re_clean) ava_re_clean = regex_must_compile(ava_re_clean_str, FALSE, TRUE); if (!ava_re_virus) ava_re_virus = regex_must_compile(ava_re_virus_str, FALSE, TRUE); +if (!ava_re_error) + ava_re_error = regex_must_compile(ava_re_error_str, FALSE, TRUE); #endif #ifndef DISABLE_MAL_FFROT6D if (!fprot6d_re_error) |