diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2018-11-21 17:56:18 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2018-11-21 17:56:18 +0000 |
commit | 07be62423bd57708f597bc9624fed0140f3b0d53 (patch) | |
tree | b3eb468fba910e9877a2863299aebf7ce25d9297 /src/plugins/lua/antivirus.lua | |
parent | ea0ebd9d56238426959c782a57e089adfa3f948b (diff) | |
download | rspamd-07be62423bd57708f597bc9624fed0140f3b0d53.tar.gz rspamd-07be62423bd57708f597bc9624fed0140f3b0d53.zip |
[Feature] Add kaspersky antivirus support
Diffstat (limited to 'src/plugins/lua/antivirus.lua')
-rw-r--r-- | src/plugins/lua/antivirus.lua | 159 |
1 files changed, 157 insertions, 2 deletions
diff --git a/src/plugins/lua/antivirus.lua b/src/plugins/lua/antivirus.lua index d7dfa0faf..875bd53e6 100644 --- a/src/plugins/lua/antivirus.lua +++ b/src/plugins/lua/antivirus.lua @@ -292,6 +292,41 @@ local function savapi_config(opts) return nil end +local function kaspersky_config(opts) + local kaspersky_conf = { + scan_mime_parts = true; + scan_text_mime = false; + scan_image_mime = false; + product_id = 0, + log_clean = false, + timeout = 5.0, + retransmits = 1, -- use local files, retransmits are useless + cache_expire = 3600, -- expire redis in one hour + message = default_message, + tmpdir = '/tmp', + prefix = 'rs_ak', + } + + kaspersky_conf = lua_util.override_defaults(kaspersky_conf, opts) + + if not kaspersky_conf['servers'] then + rspamd_logger.errx(rspamd_config, 'no servers defined') + + return nil + end + + kaspersky_conf['upstreams'] = upstream_list.create(rspamd_config, + kaspersky_conf['servers'], 0) + + if kaspersky_conf['upstreams'] then + return kaspersky_conf + end + + rspamd_logger.errx(rspamd_config, 'cannot parse servers %s', + kaspersky_conf['servers']) + return nil +end + local function message_not_too_large(task, content, rule) local max_size = tonumber(rule['max_size']) if not max_size then return true end @@ -832,6 +867,120 @@ local function savapi_check(task, content, digest, rule) end end +local function kaspersky_check(task, content, digest, rule) + local function kaspersky_check_uncached () + local upstream = rule.upstreams:get_upstream_round_robin() + local addr = upstream:get_addr() + local retransmits = rule.retransmits + local fname = string.format('%s/%s.tmp', + rule.tmpdir, rspamd_util.random_hex(32)) + local message_fd = rspamd_util.create_file(fname) + local clamav_compat_cmd = string.format("nSCAN %s\n", fname) + + if not message_fd then + rspamd_logger.errx('cannot store file for kaspersky scan: %s', fname) + return + end + + if type(content) == 'string' then + -- Create rspamd_text + local rspamd_text = require "rspamd_text" + content = rspamd_text.fromstring(content) + end + content:save_in_file(message_fd) + + -- Ensure file cleanup + task:get_mempool():add_destructor(function() + os.remove(fname) + rspamd_util.close_file(message_fd) + end) + + + local function kaspersky_callback(err, data) + if err then + -- set current upstream to fail because an error occurred + upstream:fail() + + -- retry with another upstream until retransmits exceeds + if retransmits > 0 then + + retransmits = retransmits - 1 + + -- Select a different upstream! + upstream = rule.upstreams:get_upstream_round_robin() + addr = upstream:get_addr() + + lua_util.debugm(N, task, + '%s [%s]: retry IP: %s', rule['symbol'], rule['type'], addr) + + tcp.request({ + task = task, + host = addr:to_string(), + port = addr:get_port(), + timeout = rule['timeout'], + callback = kaspersky_callback, + data = { clamav_compat_cmd }, + stop_pattern = '\n' + }) + else + rspamd_logger.errx(task, + '%s [%s]: failed to scan, maximum retransmits exceed', + rule['symbol'], rule['type']) + task:insert_result(rule['symbol_fail'], 0.0, + 'failed to scan and retransmits exceed') + end + + else + upstream:ok() + data = tostring(data) + local cached + lua_util.debugm(N, task, '%s [%s]: got reply: %s', + rule['symbol'], rule['type'], data) + if data == 'stream: OK' then + cached = 'OK' + if rule['log_clean'] then + rspamd_logger.infox(task, '%s [%s]: message or mime_part is clean', + rule['symbol'], rule['type']) + else + lua_util.debugm(N, task, '%s [%s]: message or mime_part is clean', + rule['symbol'], rule['type']) + end + else + local vname = string.match(data, ': (.+) FOUND') + if vname then + yield_result(task, rule, vname) + cached = vname + else + rspamd_logger.errx(task, 'unhandled response: %s', data) + task:insert_result(rule['symbol_fail'], 0.0, 'unhandled response') + end + end + if cached then + save_av_cache(task, digest, rule, cached) + end + end + end + + tcp.request({ + task = task, + host = addr:to_string(), + port = addr:get_port(), + timeout = rule['timeout'], + callback = kaspersky_callback, + data = { clamav_compat_cmd }, + stop_pattern = '\n' + }) + end + + if need_av_check(task, content, rule) then + if check_av_cache(task, digest, rule, kaspersky_check_uncached) then + return + else + kaspersky_check_uncached() + end + end +end + local av_types = { clamav = { configure = clamav_config, @@ -849,6 +998,10 @@ local av_types = { configure = savapi_config, check = savapi_check }, + kaspersky = { + configure = kaspersky_config, + check = kaspersky_check + } } local function add_antivirus_rule(sym, opts) @@ -857,7 +1010,7 @@ local function add_antivirus_rule(sym, opts) return nil end - if not opts['symbol'] then opts['symbol'] = sym end + if not opts['symbol'] then opts['symbol'] = sym:upper() end local cfg = av_types[opts['type']] if not opts['symbol_fail'] then @@ -943,8 +1096,10 @@ if opts and type(opts) == 'table' then redis_params = rspamd_parse_redis_server('antivirus') local has_valid = false for k, m in pairs(opts) do - if type(m) == 'table' and m['type'] and m['servers'] then + if type(m) == 'table' and m.servers then + if not m.type then m.type = k end local cb = add_antivirus_rule(k, m) + if not cb then rspamd_logger.errx(rspamd_config, 'cannot add rule: "' .. k .. '"') else |