Browse Source

Merge pull request #4079 from HeinleinSupport/lua_scanners/icap

Lua scanners/icap
tags/3.2
Vsevolod Stakhov 2 years ago
parent
commit
9a6ee04f0d
No account linked to committer's email address
1 changed files with 55 additions and 44 deletions
  1. 55
    44
      lualib/lua_scanners/icap.lua

+ 55
- 44
lualib/lua_scanners/icap.lua View File

@@ -20,10 +20,11 @@ limitations under the License.
This module contains icap access functions.
Currently tested with
- C-ICAP Squidclamav / echo
- Checkpoint Sandblast
- F-Secure Internet Gatekeeper
- Kaspersky Web Traffic Security
- Kaspersky Scan Engine 2.0
- McAfee Web Gateway 11
- McAfee Web Gateway 9/10/11
- Sophos Savdi
- Symantec (Rspamd <3.2, >=3.2 untested)
- Trend Micro IWSVA 6.0
@@ -41,6 +42,9 @@ Configuration Notes:
C-ICAP Squidclamav
scheme = "squidclamav";

Checkpoint Sandblast example:
scheme = "sandblast";

ESET Gateway Security / Antivirus for Linux example:
scheme = "scan";

@@ -58,7 +62,7 @@ Kaspersky Web Traffic Security (as configured in kavicapd.xml):
scheme = "resp";
x_client_header = true;

McAfee Web Gateway 11 (Headers must be activated with personal extra Rules)
McAfee Web Gateway 10/11 (Headers must be activated with personal extra Rules)
scheme = "respmod";
x_client_header = true;

@@ -168,6 +172,7 @@ local function icap_check(task, content, digest, rule, maybe_part)
local http_headers = {}
local req_headers = {}
local tcp_options = {}
local threat_table = {}

-- Build extended User Agent
if rule.user_agent == "extended" then
@@ -231,7 +236,7 @@ local function icap_check(task, content, digest, rule, maybe_part)
else
rspamd_logger.errx(task, '%s: failed to scan, maximum retransmits '..
'exceed - error: %s', rule.log_prefix, err_m or '')
common.yield_result(task, rule, 'failed - error: ' .. err_m or '',
common.yield_result(task, rule, string.format('failed - error: %s', err_m),
0.0, 'fail', maybe_part)
end
end
@@ -341,9 +346,21 @@ local function icap_check(task, content, digest, rule, maybe_part)
return icap_headers
end

local function icap_parse_result(headers)
local function threat_table_add(icap_threat, maybe_split)

if maybe_split and string.find(icap_threat, ',') then
local threats = lua_util.str_split(string.gsub(icap_threat, "%s", ""), ',') or {}

for _,v in ipairs(threats) do
table.insert(threat_table, v)
end
else
table.insert(threat_table, icap_threat)
end
return true
end

local threat_string = {}
local function icap_parse_result(headers)

--[[
@ToDo: handle type in response
@@ -389,7 +406,7 @@ local function icap_check(task, content, digest, rule, maybe_part)
icap: X-Virus-ID: Trojaner
icap: X-Response-Info: Blocked

McAfee Web Gateway 11 (Headers must be activated with personal extra Rules)
McAfee Web Gateway 10/11 (Headers must be activated with personal extra Rules)
icap: X-Virus-ID: EICAR test file
icap: X-Media-Type: text/plain
icap: X-Block-Result: 80
@@ -409,32 +426,32 @@ local function icap_check(task, content, digest, rule, maybe_part)
local _,_,icap_type,_,icap_threat =
headers['x-infection-found']:find("Type=(.-); Resolution=(.-); Threat=(.-);$")

if not icap_type or icap_type == 2 then
-- error returned
-- Type=2 is typical for scan error returns
if icap_type and icap_type == '2' then
lua_util.debugm(rule.name, task,
'%s: icap error X-Infection-Found: %s', rule.log_prefix, icap_threat)
common.yield_result(task, rule, icap_threat, 0,
'fail', maybe_part)
return true
else
elseif icap_threat ~= nil then
lua_util.debugm(rule.name, task,
'%s: icap X-Infection-Found: %s', rule.log_prefix, icap_threat)
table.insert(threat_string, icap_threat)
threat_table_add(icap_threat, false)
-- stupid workaround for unuseable x-infection-found header
-- but also x-virus-name set (McAfee Web Gateway 9)
elseif not icap_threat and headers['x-virus-name'] then
threat_table_add(headers['x-virus-name'], true)
else
threat_table_add(headers['x-infection-found'], true)
end

elseif headers['x-virus-name'] and headers['x-virus-name'] ~= "no threats" then
lua_util.debugm(rule.name, task,
'%s: icap X-Virus-Name: %s', rule.log_prefix, headers['x-virus-name'])
threat_table_add(headers['x-virus-name'], true)
elseif headers['x-virus-id'] and headers['x-virus-id'] ~= "no threats" then
lua_util.debugm(rule.name, task,
'%s: icap X-Virus-ID: %s', rule.log_prefix, headers['x-virus-id'])

if string.find(headers['x-virus-id'], ', ') then
local vnames = lua_util.str_split(string.gsub(headers['x-virus-id'], "%s", ""), ',') or {}

for _,v in ipairs(vnames) do
table.insert(threat_string, v)
end
else
table.insert(threat_string, headers['x-virus-id'])
end
threat_table_add(headers['x-virus-id'], true)
-- FSecure X-Headers
elseif headers['x-fsecure-scan-result'] and headers['x-fsecure-scan-result'] ~= "clean" then

@@ -452,33 +469,29 @@ local function icap_check(task, content, digest, rule, maybe_part)
'%s: icap X-FSecure-Infection-Name (X-FSecure-Infected-Filename): %s (%s)',
rule.log_prefix, infection_name, infected_filename)

if string.find(infection_name, ', ') then
local vnames = lua_util.str_split(infection_name, ',') or {}

for _,v in ipairs(vnames) do
table.insert(threat_string, v)
end
else
table.insert(threat_string, infection_name)
end
threat_table_add(infection_name, true)
-- McAfee Web Gateway manual extra headers
elseif headers['x-mwg-block-reason'] and headers['x-mwg-block-reason'] ~= "" then
table.insert(threat_string, headers['x-mwg-block-reason'])
threat_table_add(headers['x-mwg-block-reason'], false)
-- Sophos SAVDI special http headers
elseif headers['x-blocked'] and headers['x-blocked'] ~= "" then
table.insert(threat_string, headers['x-blocked'])
threat_table_add(headers['x-blocked'], false)
elseif headers['x-block-reason'] and headers['x-block-reason'] ~= "" then
threat_table_add(headers['x-block-reason'], false)
-- last try HTTP [4]xx return
elseif headers.http and string.find(headers.http, '^HTTP%/[12]%.. [4]%d%d') then
local message = string.format("pseudo-virus (blocked): %s", string.gsub(headers.http, 'HTTP%/[12]%.. ', ''))
table.insert(threat_string, message)
elseif rule.use_http_3xx_as_threat and headers.http and string.find(headers.http, '^HTTP%/[12]%.. [3]%d%d') then
local message = string.format("pseudo-virus (redirect): %s", string.gsub(headers.http, 'HTTP%/[12]%.. ', ''))
table.insert(threat_string, message)
threat_table_add(
string.format("pseudo-virus (blocked): %s", string.gsub(headers.http, 'HTTP%/[12]%.. ', '')), false)
elseif rule.use_http_3xx_as_threat and headers.http and string.find(headers.http, '^HTTP%/[12]%.. [3]%d%d')
then

threat_table_add(
string.format("pseudo-virus (redirect): %s", string.gsub(headers.http, 'HTTP%/[12]%.. ', '')), false)
end

if #threat_string > 0 then
common.yield_result(task, rule, threat_string, rule.default_score, nil, maybe_part)
common.save_cache(task, digest, rule, threat_string, rule.default_score, maybe_part)
if #threat_table > 0 then
common.yield_result(task, rule, threat_table, rule.default_score, nil, maybe_part)
common.save_cache(task, digest, rule, threat_table, rule.default_score, maybe_part)
return true
else
return false
@@ -505,8 +518,7 @@ local function icap_check(task, content, digest, rule, maybe_part)
else
rspamd_logger.errx(task, '%s: unhandled response |%s|',
rule.log_prefix, string.gsub(result, "\r\n", ", "))
common.yield_result(task, rule,
'unhandled icap response: ' .. icap_http_headers.icap or "-",
common.yield_result(task, rule, string.format('unhandled icap response: %s', icap_http_headers.icap),
0.0, 'fail', maybe_part)
end
end
@@ -560,8 +572,7 @@ local function icap_check(task, content, digest, rule, maybe_part)
else
rspamd_logger.errx(task, '%s: unhandled response |%s|',
rule.log_prefix, string.gsub(result, "\r\n", ", "))
common.yield_result(task, rule,
'unhandled icap response: ' .. icap_headers.icap or "-",
common.yield_result(task, rule, string.format('unhandled icap response: %s', icap_headers.icap),
0.0, 'fail', maybe_part)
end
end

Loading…
Cancel
Save