aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lualib/lua_dkim_tools.lua112
-rw-r--r--src/fuzzy_storage.c14
-rw-r--r--src/plugins/lua/arc.lua198
3 files changed, 197 insertions, 127 deletions
diff --git a/lualib/lua_dkim_tools.lua b/lualib/lua_dkim_tools.lua
index b7f520fae..69c9462b5 100644
--- a/lualib/lua_dkim_tools.lua
+++ b/lualib/lua_dkim_tools.lua
@@ -13,7 +13,7 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-]]--
+]] --
local exports = {}
@@ -33,7 +33,7 @@ local function check_violation(N, task, domain)
if task:has_symbol(sym_check) then
local sym = task:get_symbol(sym_check)[1]
logger.infox(task, 'skip signing for %s: violation %s found: %s',
- domain, sym_check, sym.options)
+ domain, sym_check, sym.options)
return false
end
@@ -92,7 +92,6 @@ local function parse_dkim_http_headers(N, task, settings)
local key = task:get_request_header(headers.key_header)
if not (domain and selector and key) then
-
logger.errx(task, 'missing required headers to sign email')
return false, {}
end
@@ -258,14 +257,14 @@ local function prepare_dkim_signing(N, task, settings)
-- OpenDKIM style
if is_skip_sign() then
lua_util.debugm(N, task,
- 'skip signing: is_sign_network: %s, is_authed: %s, is_local: %s',
- is_sign_networks, is_authed, is_local)
+ 'skip signing: is_sign_network: %s, is_authed: %s, is_local: %s',
+ is_sign_networks, is_authed, is_local)
return false, {}
end
if not hfrom or not hfrom[1] or not hfrom[1].addr then
lua_util.debugm(N, task,
- 'signing_table: cannot get data when no header from is presented')
+ 'signing_table: cannot get data when no header from is presented')
return false, {}
end
local sign_entry = settings.signing_table:get_key(hfrom[1].addr:lower())
@@ -273,7 +272,7 @@ local function prepare_dkim_signing(N, task, settings)
if sign_entry then
-- Check opendkim style entries
lua_util.debugm(N, task,
- 'signing_table: found entry for %s: %s', hfrom[1].addr, sign_entry)
+ 'signing_table: found entry for %s: %s', hfrom[1].addr, sign_entry)
if sign_entry == '%' then
sign_entry = hdom
end
@@ -291,7 +290,7 @@ local function prepare_dkim_signing(N, task, settings)
if not selector then
logger.errx(task, 'no selector defined for sign_entry %s, key_entry %s',
- sign_entry, key_entry)
+ sign_entry, key_entry)
return false, {}
end
@@ -305,11 +304,11 @@ local function prepare_dkim_signing(N, task, settings)
if st:sub(1, 1) == '/' or st == './' or st == '..' then
res.key = parts[2]:gsub('%%', hdom)
lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, key file=%s',
- hdom, selector, res.domain, res.key)
+ hdom, selector, res.domain, res.key)
else
res.rawkey = parts[2] -- No sanity check here
lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, raw key used',
- hdom, selector, res.domain)
+ hdom, selector, res.domain)
end
return true, { res }
@@ -327,56 +326,56 @@ local function prepare_dkim_signing(N, task, settings)
if st:sub(1, 1) == '/' or st == './' or st == '..' then
res.key = parts[3]:gsub('%%', hdom)
lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, key file=%s',
- hdom, selector, res.domain, res.key)
+ hdom, selector, res.domain, res.key)
else
res.rawkey = parts[3] -- No sanity check here
lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, raw key used',
- hdom, selector, res.domain)
+ hdom, selector, res.domain)
end
return true, { res }
else
logger.errx(task, 'invalid key entry for sign entry %s: %s; when signing %s domain',
- sign_entry, key_entry, hdom)
+ sign_entry, key_entry, hdom)
return false, {}
end
elseif settings.use_vault then
-- Sign table is presented, the rest is covered by vault
lua_util.debugm(N, task, 'check vault for %s, by sign entry %s, key entry is missing',
- hdom, sign_entry)
+ hdom, sign_entry)
return true, {
domain = sign_entry,
vault = true
}
else
logger.errx(task, 'missing key entry for sign entry %s; when signing %s domain',
- sign_entry, hdom)
+ sign_entry, hdom)
return false, {}
end
else
logger.errx(task, 'cannot get key entry for signing entry %s, when signing %s domain',
- sign_entry, hdom)
+ sign_entry, hdom)
return false, {}
end
else
lua_util.debugm(N, task,
- 'signing_table: no entry for %s', hfrom[1].addr)
+ 'signing_table: no entry for %s', hfrom[1].addr)
return false, {}
end
else
if settings.use_domain_sign_networks and is_sign_networks then
dkim_domain = get_dkim_domain('use_domain_sign_networks')
lua_util.debugm(N, task,
- 'sign_networks: use domain(%s) for signature: %s',
- settings.use_domain_sign_networks, dkim_domain)
+ 'sign_networks: use domain(%s) for signature: %s',
+ settings.use_domain_sign_networks, dkim_domain)
elseif settings.use_domain_sign_local and is_local then
dkim_domain = get_dkim_domain('use_domain_sign_local')
lua_util.debugm(N, task, 'local: use domain(%s) for signature: %s',
- settings.use_domain_sign_local, dkim_domain)
+ settings.use_domain_sign_local, dkim_domain)
elseif settings.use_domain_sign_inbound and not is_local and not auser then
dkim_domain = get_dkim_domain('use_domain_sign_inbound')
lua_util.debugm(N, task, 'inbound: use domain(%s) for signature: %s',
- settings.use_domain_sign_inbound, dkim_domain)
+ settings.use_domain_sign_inbound, dkim_domain)
elseif settings.use_domain_custom then
if type(settings.use_domain_custom) == 'string' then
-- Load custom function
@@ -387,10 +386,10 @@ local function prepare_dkim_signing(N, task, settings)
settings.use_domain_custom = res_or_err
dkim_domain = settings.use_domain_custom(task)
lua_util.debugm(N, task, 'use custom domain for signing: %s',
- dkim_domain)
+ dkim_domain)
else
logger.errx(task, 'cannot load dkim domain custom script: invalid type: %s, expected function',
- type(res_or_err))
+ type(res_or_err))
settings.use_domain_custom = nil
end
else
@@ -400,12 +399,12 @@ local function prepare_dkim_signing(N, task, settings)
else
dkim_domain = settings.use_domain_custom(task)
lua_util.debugm(N, task, 'use custom domain for signing: %s',
- dkim_domain)
+ dkim_domain)
end
else
dkim_domain = get_dkim_domain('use_domain')
lua_util.debugm(N, task, 'use domain(%s) for signature: %s',
- settings.use_domain, dkim_domain)
+ settings.use_domain, dkim_domain)
end
end
@@ -467,7 +466,7 @@ local function prepare_dkim_signing(N, task, settings)
})
else
lua_util.debugm(N, task, 'domain %s is not designated for vault',
- dkim_domain)
+ dkim_domain)
end
else
-- TODO: try every domain in the vault
@@ -501,7 +500,7 @@ local function prepare_dkim_signing(N, task, settings)
if ret then
table.insert(p, k)
lua_util.debugm(N, task, 'using mempool selector %s with key %s',
- k.selector, k.key)
+ k.selector, k.key)
end
end
@@ -530,11 +529,11 @@ local function prepare_dkim_signing(N, task, settings)
if not settings.use_redis then
insert_or_update_prop(N, task, p, 'key',
- 'default path', settings.path)
+ 'default path', settings.path)
end
insert_or_update_prop(N, task, p, 'selector',
- 'default selector', settings.selector)
+ 'default selector', settings.selector)
if settings.check_violation then
if not check_violation(N, task, p.domain) then
@@ -543,7 +542,7 @@ local function prepare_dkim_signing(N, task, settings)
end
insert_or_update_prop(N, task, p, 'domain', 'dkim_domain',
- dkim_domain)
+ dkim_domain)
return #p > 0 and true or false, p
end
@@ -560,53 +559,53 @@ exports.sign_using_redis = function(N, task, settings, selectors, sign_func, err
local function redis_key_cb(err, data)
if err then
err_func(string.format("cannot make request to load DKIM key for %s: %s",
- rk, err))
+ rk, err))
elseif type(data) ~= 'string' then
lua_util.debugm(N, task, "missing DKIM key for %s", rk)
else
p.rawkey = data
lua_util.debugm(N, task, 'found and parsed key for %s:%s in Redis',
- p.domain, p.selector)
+ p.domain, p.selector)
sign_func(task, p)
end
end
local rret = lua_redis.redis_make_request(task,
- settings.redis_params, -- connect params
- rk, -- hash key
- false, -- is write
- redis_key_cb, --callback
- 'HGET', -- command
- { settings.key_prefix, rk } -- arguments
+ settings.redis_params, -- connect params
+ rk, -- hash key
+ false, -- is write
+ redis_key_cb, --callback
+ 'HGET', -- command
+ { settings.key_prefix, rk } -- arguments
)
if not rret then
err_func(task,
- string.format("cannot make request to load DKIM key for %s", rk))
+ string.format("cannot make request to load DKIM key for %s", rk))
end
end
for _, p in ipairs(selectors) do
if settings.selector_prefix then
logger.infox(task, "using selector prefix '%s' for domain '%s'",
- settings.selector_prefix, p.domain);
+ settings.selector_prefix, p.domain);
local function redis_selector_cb(err, data)
if err or type(data) ~= 'string' then
err_func(task, string.format("cannot make request to load DKIM selector for domain %s: %s",
- p.domain, err))
+ p.domain, err))
else
try_redis_key(data, p)
end
end
local rret = lua_redis.redis_make_request(task,
- settings.redis_params, -- connect params
- p.domain, -- hash key
- false, -- is write
- redis_selector_cb, --callback
- 'HGET', -- command
- { settings.selector_prefix, p.domain } -- arguments
+ settings.redis_params, -- connect params
+ p.domain, -- hash key
+ false, -- is write
+ redis_selector_cb, --callback
+ 'HGET', -- command
+ { settings.selector_prefix, p.domain } -- arguments
)
if not rret then
err_func(task, string.format("cannot make Redis request to load DKIM selector for domain %s",
- p.domain))
+ p.domain))
end
else
try_redis_key(p.selector, p)
@@ -619,25 +618,25 @@ exports.sign_using_vault = function(N, task, settings, selector, sign_func, err_
local ucl = require "ucl"
local full_url = string.format('%s/v1/%s/%s',
- settings.vault_url, settings.vault_path or 'dkim', selector.domain)
+ settings.vault_url, settings.vault_path or 'dkim', selector.domain)
local upstream_list = lua_util.http_upstreams_by_url(rspamd_config:get_mempool(), settings.vault_url)
local function vault_callback(err, code, body, _)
if code ~= 200 then
err_func(task, string.format('cannot request data from the vault url: %s; %s (%s)',
- full_url, err, body))
+ full_url, err, body))
else
local parser = ucl.parser()
local res, parser_err = parser:parse_string(body)
if not res then
err_func(task, string.format('vault reply for %s (data=%s) cannot be parsed: %s',
- full_url, body, parser_err))
+ full_url, body, parser_err))
else
local obj = parser:get_object()
if not obj or not obj.data then
err_func(task, string.format('vault reply for %s (data=%s) is invalid, no data',
- full_url, body))
+ full_url, body))
else
local elts = obj.data.selectors or {}
local errs = {}
@@ -675,13 +674,13 @@ exports.sign_using_vault = function(N, task, settings, selector, sign_func, err_
alg = p.alg,
}
lua_util.debugm(N, task, 'found and parsed key for %s:%s in Vault',
- dkim_sign_data.domain, dkim_sign_data.selector)
+ dkim_sign_data.domain, dkim_sign_data.selector)
nvalid = nvalid + 1
sign_func(task, dkim_sign_data)
end, fun.filter(is_selector_valid, elts))
for _, e in errs do
lua_util.debugm(N, task, 'error found during processing Vault selectors: %s:%s',
- e[1], e[2])
+ e[1], e[2])
end
if nvalid == 0 then
@@ -707,7 +706,7 @@ exports.sign_using_vault = function(N, task, settings, selector, sign_func, err_
if not ret then
err_func(task, string.format("cannot make HTTP request to load DKIM data domain %s",
- selector.domain))
+ selector.domain))
end
end
@@ -732,8 +731,7 @@ exports.process_signing_settings = function(N, settings, opts)
selector_map = { 'map', 'DKIM selectors' },
signing_table = { 'glob', 'DKIM signing table' },
key_table = { 'glob', 'DKIM keys table' },
- vault_domains = { 'glob', 'DKIM signing domains in vault' },
- whitelisted_signers_map = { 'set', 'ARC trusted signers domains' }
+ vault_domains = { 'glob', 'DKIM signing domains in vault' }
}
for k, v in pairs(opts) do
local maybe_map = maps_opts[k]
diff --git a/src/fuzzy_storage.c b/src/fuzzy_storage.c
index 58d123712..d6836df3b 100644
--- a/src/fuzzy_storage.c
+++ b/src/fuzzy_storage.c
@@ -1386,6 +1386,20 @@ rspamd_fuzzy_check_callback(struct rspamd_fuzzy_reply *result, void *ud)
}
}
+ /* Check if the returned hash from fuzzy matching should be skipped */
+ if (session->ctx->skip_hashes && result->v1.value > 0) {
+ char hexbuf[sizeof(result->digest) * 2 + 1];
+ rspamd_encode_hex_buf(result->digest, sizeof(result->digest),
+ hexbuf, sizeof(hexbuf) - 1);
+ hexbuf[sizeof(hexbuf) - 1] = '\0';
+
+ if (rspamd_match_hash_map(session->ctx->skip_hashes,
+ hexbuf, sizeof(hexbuf) - 1)) {
+ result->v1.value = 401;
+ result->v1.prob = 0.0f;
+ }
+ }
+
if (!isnan(session->ctx->delay) &&
rspamd_match_radix_map_addr(session->ctx->delay_whitelist,
session->addr) == NULL) {
diff --git a/src/plugins/lua/arc.lua b/src/plugins/lua/arc.lua
index 45da1f5a2..954583ed0 100644
--- a/src/plugins/lua/arc.lua
+++ b/src/plugins/lua/arc.lua
@@ -72,12 +72,13 @@ local settings = {
use_domain = 'header',
use_esld = true,
use_redis = false,
- key_prefix = 'arc_keys', -- default hash name
- reuse_auth_results = false, -- Reuse the existing authentication results
+ key_prefix = 'arc_keys', -- default hash name
+ reuse_auth_results = false, -- Reuse the existing authentication results
whitelisted_signers_map = nil, -- Trusted signers domains
- adjust_dmarc = true, -- Adjust DMARC rejected policy for trusted forwarders
- allowed_ids = nil, -- Allowed settings id
- forbidden_ids = nil, -- Banned settings id
+ whitelist = nil, -- Domains with broken ARC implementations to trust despite validation failures
+ adjust_dmarc = true, -- Adjust DMARC rejected policy for trusted forwarders
+ allowed_ids = nil, -- Allowed settings id
+ forbidden_ids = nil, -- Banned settings id
}
-- To match normal AR
@@ -86,15 +87,15 @@ local ar_settings = lua_auth_results.default_settings
local function parse_arc_header(hdr, target, is_aar)
-- Split elements by ';' and trim spaces
local arr = fun.totable(fun.map(
- function(val)
- return fun.totable(fun.map(lua_util.rspamd_str_trim,
- fun.filter(function(v)
- return v and #v > 0
- end,
- lua_util.rspamd_str_split(val.decoded, ';')
- )
- ))
- end, hdr
+ function(val)
+ return fun.totable(fun.map(lua_util.rspamd_str_trim,
+ fun.filter(function(v)
+ return v and #v > 0
+ end,
+ lua_util.rspamd_str_split(val.decoded, ';')
+ )
+ ))
+ end, hdr
))
-- v[1] is the key and v[2] is the value
@@ -115,11 +116,11 @@ local function parse_arc_header(hdr, target, is_aar)
if not is_aar then
-- For normal ARC headers we split by kv pair, like k=v
fun.each(function(v)
- fill_arc_header_table(v, target[i])
- end,
- fun.map(function(elt)
- return lua_util.rspamd_str_split(elt, '=')
- end, elts)
+ fill_arc_header_table(v, target[i])
+ end,
+ fun.map(function(elt)
+ return lua_util.rspamd_str_split(elt, '=')
+ end, elts)
)
else
-- For AAR we check special case of i=%d and pass everything else to
@@ -156,14 +157,14 @@ local function arc_validate_seals(task, seals, sigs, seal_headers, sig_headers)
for i = 1, #seals do
if (sigs[i].i or 0) ~= i then
fail_reason = string.format('bad i for signature: %d, expected %d; d=%s',
- sigs[i].i, i, sigs[i].d)
+ sigs[i].i, i, sigs[i].d)
rspamd_logger.infox(task, fail_reason)
task:insert_result(arc_symbols['invalid'], 1.0, fail_reason)
return false, fail_reason
end
if (seals[i].i or 0) ~= i then
fail_reason = string.format('bad i for seal: %d, expected %d; d=%s',
- seals[i].i, i, seals[i].d)
+ seals[i].i, i, seals[i].d)
rspamd_logger.infox(task, fail_reason)
task:insert_result(arc_symbols['invalid'], 1.0, fail_reason)
return false, fail_reason
@@ -207,7 +208,7 @@ local function arc_callback(task)
if #arc_sig_headers ~= #arc_seal_headers then
-- We mandate that count of seals is equal to count of signatures
rspamd_logger.infox(task, 'number of seals (%s) is not equal to number of signatures (%s)',
- #arc_seal_headers, #arc_sig_headers)
+ #arc_seal_headers, #arc_sig_headers)
task:insert_result(arc_symbols['invalid'], 1.0, 'invalid count of seals and signatures')
return
end
@@ -249,7 +250,7 @@ local function arc_callback(task)
-- Now check sanity of what we have
local valid, validation_error = arc_validate_seals(task, cbdata.seals, cbdata.sigs,
- arc_seal_headers, arc_sig_headers)
+ arc_seal_headers, arc_sig_headers)
if not valid then
task:cache_set('arc-failure', validation_error)
return
@@ -267,12 +268,20 @@ local function arc_callback(task)
local function gen_arc_seal_cb(index, sig)
return function(_, res, err, domain)
lua_util.debugm(N, task, 'checked arc seal: %s(%s), %s processed',
- res, err, index)
+ res, err, index)
if not res then
- cbdata.res = 'fail'
- if err and domain then
- table.insert(cbdata.errors, string.format('sig:%s:%s', domain, err))
+ -- Check if this domain is whitelisted for broken ARC implementations
+ if settings.whitelist and domain and settings.whitelist:get_key(domain) then
+ rspamd_logger.infox(task, 'ARC seal validation failed for whitelisted domain %s, treating as valid: %s',
+ domain, err)
+ lua_util.debugm(N, task, 'whitelisted domain %s ARC seal failure ignored', domain)
+ res = true -- Treat as valid to continue the chain
+ else
+ cbdata.res = 'fail'
+ if err and domain then
+ table.insert(cbdata.errors, string.format('sig:%s:%s', domain, err))
+ end
end
end
@@ -283,7 +292,7 @@ local function arc_callback(task)
local cur_aar = cbdata.ars[index]
if not cur_aar then
rspamd_logger.warnx(task, "cannot find Arc-Authentication-Results for trusted " ..
- "forwarder %s on i=%s", domain, cbdata.index)
+ "forwarder %s on i=%s", domain, cbdata.index)
else
task:cache_set(AR_TRUSTED_CACHE_KEY, cur_aar)
local seen_dmarc
@@ -309,20 +318,20 @@ local function arc_callback(task)
end
end
task:insert_result(arc_symbols.trusted_allow, mult,
- string.format('%s:s=%s:i=%d', domain, sig.s, index))
+ string.format('%s:s=%s:i=%d', domain, sig.s, index))
end
end
if index == #arc_sig_headers then
if cbdata.res == 'success' then
local arc_allow_result = string.format('%s:s=%s:i=%d',
- domain, sig.s, index)
+ domain, sig.s, index)
task:insert_result(arc_symbols.allow, 1.0, arc_allow_result)
task:cache_set('arc-allow', arc_allow_result)
else
task:insert_result(arc_symbols.reject, 1.0,
- rspamd_logger.slog('seal check failed: %s, %s', cbdata.res,
- cbdata.errors))
+ rspamd_logger.slog('seal check failed: %s, %s', cbdata.res,
+ cbdata.errors))
end
end
end
@@ -330,12 +339,20 @@ local function arc_callback(task)
local function arc_signature_cb(_, res, err, domain)
lua_util.debugm(N, task, 'checked arc signature %s: %s(%s)',
- domain, res, err)
+ domain, res, err)
if not res then
- cbdata.res = 'fail'
- if err and domain then
- table.insert(cbdata.errors, string.format('sig:%s:%s', domain, err))
+ -- Check if this domain is whitelisted for broken ARC implementations
+ if settings.whitelist and domain and settings.whitelist:get_key(domain) then
+ rspamd_logger.infox(task, 'ARC signature validation failed for whitelisted domain %s, treating as valid: %s',
+ domain, err)
+ lua_util.debugm(N, task, 'whitelisted domain %s ARC signature failure ignored', domain)
+ res = true -- Treat as valid to continue the chain
+ else
+ cbdata.res = 'fail'
+ if err and domain then
+ table.insert(cbdata.errors, string.format('sig:%s:%s', domain, err))
+ end
end
end
if cbdata.res == 'success' then
@@ -343,17 +360,24 @@ local function arc_callback(task)
for i, sig in ipairs(cbdata.seals) do
local ret, lerr = dkim_verify(task, sig.header, gen_arc_seal_cb(i, sig), 'arc-seal')
if not ret then
- cbdata.res = 'fail'
- table.insert(cbdata.errors, string.format('seal:%s:s=%s:i=%s:%s',
+ -- Check if this domain is whitelisted for broken ARC implementations
+ if settings.whitelist and sig.d and settings.whitelist:get_key(sig.d) then
+ rspamd_logger.infox(task, 'ARC seal dkim_verify failed for whitelisted domain %s, treating as valid: %s',
+ sig.d, lerr)
+ lua_util.debugm(N, task, 'whitelisted domain %s ARC seal dkim_verify failure ignored', sig.d)
+ else
+ cbdata.res = 'fail'
+ table.insert(cbdata.errors, string.format('seal:%s:s=%s:i=%s:%s',
sig.d or '', sig.s or '', sig.i or '', lerr))
- lua_util.debugm(N, task, 'checked arc seal %s: %s(%s), %s processed',
+ lua_util.debugm(N, task, 'checked arc seal %s: %s(%s), %s processed',
sig.d, ret, lerr, i)
+ end
end
end
else
task:insert_result(arc_symbols['reject'], 1.0,
- rspamd_logger.slog('signature check failed: %s, %s', cbdata.res,
- cbdata.errors))
+ rspamd_logger.slog('signature check failed: %s, %s', cbdata.res,
+ cbdata.errors))
end
end
@@ -397,25 +421,33 @@ local function arc_callback(task)
is "fail" and the algorithm stops here.
9. If the algorithm reaches this step, then the Chain Validation
Status is "pass", and the algorithm is complete.
- ]]--
+ ]] --
local processed = 0
local sig = cbdata.sigs[#cbdata.sigs] -- last AMS
local ret, err = dkim_verify(task, sig.header, arc_signature_cb, 'arc-sign')
if not ret then
- cbdata.res = 'fail'
- table.insert(cbdata.errors, string.format('sig:%s:%s', sig.d or '', err))
+ -- Check if this domain is whitelisted for broken ARC implementations
+ if settings.whitelist and sig.d and settings.whitelist:get_key(sig.d) then
+ rspamd_logger.infox(task, 'ARC signature dkim_verify failed for whitelisted domain %s, treating as valid: %s',
+ sig.d, err)
+ lua_util.debugm(N, task, 'whitelisted domain %s ARC signature dkim_verify failure ignored', sig.d)
+ processed = processed + 1
+ else
+ cbdata.res = 'fail'
+ table.insert(cbdata.errors, string.format('sig:%s:%s', sig.d or '', err))
+ end
else
processed = processed + 1
lua_util.debugm(N, task, 'processed arc signature %s[%s]: %s(%s), %s total',
- sig.d, sig.i, ret, err, #cbdata.seals)
+ sig.d, sig.i, ret, err, #cbdata.seals)
end
if processed == 0 then
task:insert_result(arc_symbols['reject'], 1.0,
- rspamd_logger.slog('cannot verify %s of %s signatures: %s',
- #arc_sig_headers - processed, #arc_sig_headers, cbdata.errors))
+ rspamd_logger.slog('cannot verify %s of %s signatures: %s',
+ #arc_sig_headers - processed, #arc_sig_headers, cbdata.errors))
end
end
@@ -538,13 +570,13 @@ local function arc_sign_seal(task, params, header)
for i = 1, #arc_seals, 1 do
if arc_auth_results[i] then
local s = dkim_canonicalize('ARC-Authentication-Results',
- arc_auth_results[i].raw_header)
+ arc_auth_results[i].raw_header)
sha_ctx:update(s)
lua_util.debugm(N, task, 'update signature with header: %s', s)
end
if arc_sigs[i] then
local s = dkim_canonicalize('ARC-Message-Signature',
- arc_sigs[i].raw_header)
+ arc_sigs[i].raw_header)
sha_ctx:update(s)
lua_util.debugm(N, task, 'update signature with header: %s', s)
end
@@ -557,16 +589,16 @@ local function arc_sign_seal(task, params, header)
end
header = lua_util.fold_header(task,
- 'ARC-Message-Signature',
- header)
+ 'ARC-Message-Signature',
+ header)
cur_auth_results = string.format('i=%d; %s', cur_idx, cur_auth_results)
cur_auth_results = lua_util.fold_header(task,
- 'ARC-Authentication-Results',
- cur_auth_results, ';')
+ 'ARC-Authentication-Results',
+ cur_auth_results, ';')
local s = dkim_canonicalize('ARC-Authentication-Results',
- cur_auth_results)
+ cur_auth_results)
sha_ctx:update(s)
lua_util.debugm(N, task, 'update signature with header: %s', s)
s = dkim_canonicalize('ARC-Message-Signature', header)
@@ -574,10 +606,10 @@ local function arc_sign_seal(task, params, header)
lua_util.debugm(N, task, 'update signature with header: %s', s)
local cur_arc_seal = string.format('i=%d; s=%s; d=%s; t=%d; a=rsa-sha256; cv=%s; b=',
- cur_idx,
- params.selector,
- params.domain,
- math.floor(rspamd_util.get_time()), params.arc_cv)
+ cur_idx,
+ params.selector,
+ params.domain,
+ math.floor(rspamd_util.get_time()), params.arc_cv)
s = string.format('%s:%s', 'arc-seal', cur_arc_seal)
sha_ctx:update(s)
lua_util.debugm(N, task, 'initial update signature with header: %s', s)
@@ -591,20 +623,23 @@ local function arc_sign_seal(task, params, header)
local sig = rspamd_rsa.sign_memory(privkey, sha_ctx:bin())
cur_arc_seal = string.format('%s%s', cur_arc_seal,
- sig:base64(70, nl_type))
+ sig:base64(70, nl_type))
lua_mime.modify_headers(task, {
add = {
['ARC-Authentication-Results'] = { order = 1, value = cur_auth_results },
['ARC-Message-Signature'] = { order = 1, value = header },
- ['ARC-Seal'] = { order = 1, value = lua_util.fold_header(task,
- 'ARC-Seal', cur_arc_seal) }
+ ['ARC-Seal'] = {
+ order = 1,
+ value = lua_util.fold_header(task,
+ 'ARC-Seal', cur_arc_seal)
+ }
},
-- RFC requires a strict order for these headers to be inserted
order = { 'ARC-Authentication-Results', 'ARC-Message-Signature', 'ARC-Seal' },
})
task:insert_result(settings.sign_symbol, 1.0,
- string.format('%s:s=%s:i=%d', params.domain, params.selector, cur_idx))
+ string.format('%s:s=%s:i=%d', params.domain, params.selector, cur_idx))
end
local function prepare_arc_selector(task, sel)
@@ -668,7 +703,6 @@ local function prepare_arc_selector(task, sel)
else
default_arc_cv()
end
-
end
return true
@@ -696,18 +730,17 @@ local function do_sign(task, sign_params)
sign_params.strict_pubkey_check = not settings.allow_pubkey_mismatch
elseif not settings.allow_pubkey_mismatch then
rspamd_logger.errx(task, 'public key for domain %s/%s is not found: %s, skip signing',
- sign_params.domain, sign_params.selector, err)
+ sign_params.domain, sign_params.selector, err)
return
else
rspamd_logger.infox(task, 'public key for domain %s/%s is not found: %s',
- sign_params.domain, sign_params.selector, err)
+ sign_params.domain, sign_params.selector, err)
end
local dret, hdr = dkim_sign(task, sign_params)
if dret then
arc_sign_seal(task, sign_params, hdr)
end
-
end,
forced = true
})
@@ -768,6 +801,31 @@ end
dkim_sign_tools.process_signing_settings(N, settings, opts)
+-- Process ARC-specific maps that aren't handled by dkim_sign_tools
+local lua_maps = require "lua_maps"
+
+if opts.whitelisted_signers_map then
+ settings.whitelisted_signers_map = lua_maps.map_add_from_ucl(opts.whitelisted_signers_map, 'set',
+ 'ARC trusted signers domains')
+ if not settings.whitelisted_signers_map then
+ rspamd_logger.errx(rspamd_config, 'cannot load whitelisted_signers_map')
+ settings.whitelisted_signers_map = nil
+ else
+ rspamd_logger.infox(rspamd_config, 'loaded ARC whitelisted signers map')
+ end
+end
+
+if opts.whitelist then
+ settings.whitelist = lua_maps.map_add_from_ucl(opts.whitelist, 'set',
+ 'ARC domains with broken implementations')
+ if not settings.whitelist then
+ rspamd_logger.errx(rspamd_config, 'cannot load ARC whitelist map')
+ settings.whitelist = nil
+ else
+ rspamd_logger.infox(rspamd_config, 'loaded ARC whitelist map')
+ end
+end
+
if not dkim_sign_tools.validate_signing_settings(settings) then
rspamd_logger.infox(rspamd_config, 'mandatory parameters missing, disable arc signing')
return
@@ -780,7 +838,7 @@ if ar_opts and ar_opts.routines then
if routines['authentication-results'] then
ar_settings = lua_util.override_defaults(ar_settings,
- routines['authentication-results'])
+ routines['authentication-results'])
end
end
@@ -789,7 +847,7 @@ if settings.use_redis then
if not redis_params then
rspamd_logger.errx(rspamd_config, 'no servers are specified, ' ..
- 'but module is configured to load keys from redis, disable arc signing')
+ 'but module is configured to load keys from redis, disable arc signing')
return
end
@@ -845,9 +903,9 @@ if settings.adjust_dmarc and settings.whitelisted_signers_map then
local dmarc_fwd = ar.dmarc
if dmarc_fwd == 'pass' then
rspamd_logger.infox(task, "adjust dmarc reject score as trusted forwarder "
- .. "proved DMARC validity for %s", ar['header.from'])
+ .. "proved DMARC validity for %s", ar['header.from'])
task:adjust_result(sym_to_adjust, 0.1,
- 'ARC trusted')
+ 'ARC trusted')
end
end
end