Browse Source

[Rework] Rework and refactor forged recipients plugin

tags/2.6
Vsevolod Stakhov 3 years ago
parent
commit
2440c68b33
1 changed files with 92 additions and 46 deletions
  1. 92
    46
      src/plugins/lua/forged_recipients.lua

+ 92
- 46
src/plugins/lua/forged_recipients.lua View File

@@ -18,7 +18,14 @@ limitations under the License.
-- in mime headers

if confighelp then
return
rspamd_config:add_example(nil, 'forged_recipients',
"Check forged recipients and senders (e.g. mime and smtp recipients mismatch)",
[[
forged_recipients {
symbol_sender = "FORGED_SENDER"; # Symbol for a forged sender
symbol_rcpt = "FORGED_RECIPIENTS"; # Symbol for a forged recipients
}
]])
end

local symbol_rcpt = 'FORGED_RECIPIENTS'
@@ -29,68 +36,107 @@ local E = {}
local function check_forged_headers(task)
local auser = task:get_user()
local delivered_to = task:get_header('Delivered-To')
local smtp_rcpt = task:get_recipients(1)
local smtp_rcpts = task:get_recipients(1)
local smtp_from = task:get_from(1)
local res
local score = 1.0

if not smtp_rcpt then return end
if #smtp_rcpt == 0 then return end
if not smtp_rcpts then return end
if #smtp_rcpts == 0 then return end

local mime_rcpt = task:get_recipients({'mime','orig'})
local mime_rcpts = task:get_recipients({ 'mime', 'orig'})

if not mime_rcpt then
if not mime_rcpts then
return
elseif #mime_rcpt == 0 then
elseif #mime_rcpts == 0 then
return
end

-- Find pair for each smtp recipient in To or Cc headers
-- This cycle has O(N^2) complexity so it is better to limit number of iterations
if #smtp_rcpt > 100 or #mime_rcpt > 100 then
if #smtp_rcpts > 100 or #mime_rcpts > 100 then
-- Trim array, suggested by Anton Yuzhaninov
smtp_rcpt[100] = nil
mime_rcpt[100] = nil
smtp_rcpts[100] = nil
mime_rcpts[100] = nil
end

for _,sr in ipairs(smtp_rcpt) do
res = false
for _,mr in ipairs(mime_rcpt) do
if mr.addr and mr.addr ~= '' then
if sr['addr'] and
string.lower(mr['addr']) == string.lower(sr['addr']) then
res = true
break
elseif delivered_to and delivered_to == mr['addr'] then
-- allow alias expansion and forwarding (Postfix)
res = true
break
elseif auser and auser == sr['addr'] then
-- allow user to BCC themselves
res = true
break
elseif ((smtp_from or E)[1] or E).addr and
smtp_from[1]['addr'] == sr['addr'] then
-- allow sender to BCC themselves
res = true
break
elseif mr['user'] and sr['user'] and
string.lower(mr['user']) == string.lower(sr['user']) then
-- If we have the same username but for another domain, then
-- lower the overall score
score = score / 2
end
-- map smtp recipient domains to a list of addresses for this domain
local smtp_rcpt_domain_map = {}
local smtp_rcpt_map = {}
for _, smtp_rcpt in ipairs(smtp_rcpts) do
local addr = smtp_rcpt.addr

if addr and addr ~= '' then
local dom = string.lower(smtp_rcpt.domain)
addr = addr:lower()

local dom_map = smtp_rcpt_domain_map[dom]
if not dom_map then
dom_map = {}
smtp_rcpt_domain_map[dom] = dom_map
end

dom_map[addr] = smtp_rcpt
smtp_rcpt_map[addr] = smtp_rcpt

if auser and auser == addr then
smtp_rcpt.matched = true
end
if ((smtp_from or E)[1] or E).addr and
smtp_from[1]['addr'] == addr then
-- allow sender to BCC themselves
smtp_rcpt.matched = true
end
end
end

for _,mime_rcpt in ipairs(mime_rcpts) do
if mime_rcpt.addr and mime_rcpt.addr ~= '' then
local addr = string.lower(mime_rcpt.addr)
local dom = string.lower(mime_rcpt.domain)
local matched_smtp_addr = smtp_rcpt_map[addr]
if matched_smtp_addr then
-- Direct match, go forward
matched_smtp_addr.matched = true
mime_rcpt.matched = true
elseif delivered_to and delivered_to == addr then
mime_rcpt.matched = true
elseif auser and auser == addr then
-- allow user to BCC themselves
mime_rcpt.matched = true
else
res = true
local matched_smtp_domain = smtp_rcpt_domain_map[dom]

if matched_smtp_domain then
-- Same domain but another user, it is likely okay due to aliases substitution
mime_rcpt.matched = true
-- Special field
matched_smtp_domain._seen_mime_domain = true
end
end
end
if not res then
local mra = mime_rcpt[1].addr .. (#mime_rcpt > 1 and ' ..' or '')
local sra = smtp_rcpt[1].addr .. (#smtp_rcpt > 1 and ' ...' or '')
task:insert_result(symbol_rcpt, score, mra, sra)
break
end

-- Now go through all lists one more time and find unmatched stuff
local opts = {}
local seen_mime_unmatched = false
local seen_smtp_unmatched = false
for _,mime_rcpt in ipairs(mime_rcpts) do
if not mime_rcpt.matched then
seen_mime_unmatched = true
table.insert(opts, 'm:' .. mime_rcpt.addr)
end
end
for _,smtp_rcpt in ipairs(smtp_rcpts) do
if not smtp_rcpt.matched then
if not smtp_rcpt_domain_map[smtp_rcpt.domain]._seen_mime_domain then
seen_smtp_unmatched = true
table.insert(opts, 's:' .. smtp_rcpt.addr)
end
end
end

if seen_smtp_unmatched and seen_mime_unmatched then
task:insert_result(symbol_rcpt, 1.0, opts)
end

-- Check sender
if smtp_from and smtp_from[1] and smtp_from[1]['addr'] ~= '' then
local mime_from = task:get_from(2)

Loading…
Cancel
Save