diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-10-31 14:19:38 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2016-10-31 14:19:38 +0000 |
commit | 87a18acc470c9b204ad21c531c72cdd34976bd97 (patch) | |
tree | 352c225aab103580ffe4b8039fdbb97f86626677 | |
parent | c54e7c72b2804f8def8a082ba704f5b0164aaeca (diff) | |
download | rspamd-87a18acc470c9b204ad21c531c72cdd34976bd97.tar.gz rspamd-87a18acc470c9b204ad21c531c72cdd34976bd97.zip |
[Rework] Rework parsing of DMARC records
-rw-r--r-- | src/plugins/lua/dmarc.lua | 124 |
1 files changed, 71 insertions, 53 deletions
diff --git a/src/plugins/lua/dmarc.lua b/src/plugins/lua/dmarc.lua index f160fd28c..077b38465 100644 --- a/src/plugins/lua/dmarc.lua +++ b/src/plugins/lua/dmarc.lua @@ -53,10 +53,26 @@ local dmarc_symbols = { local redis_params = nil local dmarc_redis_key_prefix = "dmarc_" local dmarc_domain = nil -local elts_re = rspamd_regexp.create_cached("\\s*\\\\{0,1};\\s*") local dmarc_reporting = false local dmarc_actions = {} +local function gen_dmarc_grammar() + local lpeg = require "lpeg" + lpeg.locale(lpeg) + local space = lpeg.space^0 + local name = lpeg.C(lpeg.alpha^1) * space + local sep = lpeg.S(";") * space + local value = lpeg.C(lpeg.P(lpeg.graph - sep)^1) + local pair = lpeg.Cg(name * "=" * space * value) * sep^-1 + local list = lpeg.Cf(lpeg.Ct("") * pair^0, rawset) + local version = lpeg.P("v") * space * lpeg.P("=") * space * lpeg.P("DMARC1") + local record = version * space * sep * list + + return record +end + +local dmarc_grammar = gen_dmarc_grammar() + local function dmarc_report(task, spf_ok, dkim_ok, disposition) local ip = task:get_from_ip() if not ip:is_valid() then @@ -140,7 +156,8 @@ local function dmarc_callback(task) for _,r in ipairs(results) do if failed_policy then break end (function() - if not string.match(r, '^v=DMARC1[;\\][; ]') then + local elts = dmarc_grammar:match(r) + if not elts then return else if found_policy then @@ -150,66 +167,67 @@ local function dmarc_callback(task) found_policy = true end end - local elts = elts_re:split(r) if elts then - for _,e in ipairs(elts) do - dkim_pol = string.match(e, '^adkim=(.)$') - if dkim_pol then - if dkim_pol == 's' then - strict_dkim = true - elseif dkim_pol ~= 'r' then - failed_policy = 'adkim tag has invalid value: ' .. dkim_pol - return - end + local dkim_pol = elts['adkim'] + if dkim_pol then + if dkim_pol == 's' then + strict_dkim = true + elseif dkim_pol ~= 'r' then + failed_policy = 'adkim tag has invalid value: ' .. dkim_pol + return end - spf_pol = string.match(e, '^aspf=(.)$') - if spf_pol then - if spf_pol == 's' then - strict_spf = true - elseif spf_pol ~= 'r' then - failed_policy = 'aspf tag has invalid value: ' .. spf_pol - return - end + end + + local spf_pol = elts['aspf'] + if spf_pol then + if spf_pol == 's' then + strict_spf = true + elseif spf_pol ~= 'r' then + failed_policy = 'aspf tag has invalid value: ' .. spf_pol + return + end + end + + local policy = elts['p'] + if policy then + if (policy == 'reject') then + dmarc_policy = 'reject' + elseif (policy == 'quarantine') then + dmarc_policy = 'quarantine' + elseif (policy ~= 'none') then + failed_policy = 'p tag has invalid value: ' .. policy + return end - policy = string.match(e, '^p=(.+)$') - if policy then - if (policy == 'reject') then + end + + local subdomain_policy = elts['sp'] + if subdomain_policy and lookup_domain == dmarc_domain then + if (subdomain_policy == 'reject') then + if dmarc_domain ~= from[1]['domain'] then dmarc_policy = 'reject' - elseif (policy == 'quarantine') then + end + elseif (subdomain_policy == 'quarantine') then + if dmarc_domain ~= from[1]['domain'] then dmarc_policy = 'quarantine' - elseif (policy ~= 'none') then - failed_policy = 'p tag has invalid value: ' .. policy - return end - end - subdomain_policy = string.match(e, '^sp=(.+)$') - if subdomain_policy and lookup_domain == dmarc_domain then - if (subdomain_policy == 'reject') then - if dmarc_domain ~= from[1]['domain'] then - dmarc_policy = 'reject' - end - elseif (subdomain_policy == 'quarantine') then - if dmarc_domain ~= from[1]['domain'] then - dmarc_policy = 'quarantine' - end - elseif (subdomain_policy == 'none') then - if dmarc_domain ~= from[1]['domain'] then - dmarc_policy = 'none' - end - elseif (subdomain_policy ~= 'none') then - failed_policy = 'sp tag has invalid value: ' .. subdomain_policy - return + elseif (subdomain_policy == 'none') then + if dmarc_domain ~= from[1]['domain'] then + dmarc_policy = 'none' end + elseif (subdomain_policy ~= 'none') then + failed_policy = 'sp tag has invalid value: ' .. subdomain_policy + return end - pct = string.match(e, '^pct=(%d+)$') - if pct then - pct = tonumber(pct) - end + end - if not rua then - rua = string.match(e, '^rua=([^%s]+)$') - end + local pct = elts['pct'] + if pct then + pct = tonumber(pct) + end + + if not rua then + rua = elts['rua'] end end end)() @@ -241,7 +259,7 @@ local function dmarc_callback(task) local spf_ok = false local dkim_ok = false if task:has_symbol(symbols['spf_allow_symbol']) then - efrom = task:get_from(1) + local efrom = task:get_from(1) if efrom and efrom[1] and efrom[1]['domain'] then if strict_spf and rspamd_util.strequal_caseless(efrom[1]['domain'], from[1]['domain']) then spf_ok = true |