Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750
  1. --[[
  2. Copyright (c) 2020, Vsevolod Stakhov <vsevolod@highsecure.ru>
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. ]] --
  13. local rspamd_logger = require "rspamd_logger"
  14. local lua_util = require "lua_util"
  15. local dkim_sign_tools = require "lua_dkim_tools"
  16. local rspamd_util = require "rspamd_util"
  17. local rspamd_rsa_privkey = require "rspamd_rsa_privkey"
  18. local rspamd_rsa = require "rspamd_rsa"
  19. local fun = require "fun"
  20. local auth_results = require "lua_auth_results"
  21. local hash = require "rspamd_cryptobox_hash"
  22. local lua_mime = require "lua_mime"
  23. if confighelp then
  24. return
  25. end
  26. local N = 'arc'
  27. if not rspamd_plugins.dkim then
  28. rspamd_logger.errx(rspamd_config, "cannot enable arc plugin: dkim is disabled")
  29. return
  30. end
  31. local dkim_verify = rspamd_plugins.dkim.verify
  32. local dkim_sign = rspamd_plugins.dkim.sign
  33. local dkim_canonicalize = rspamd_plugins.dkim.canon_header_relaxed
  34. local redis_params
  35. if not dkim_verify or not dkim_sign or not dkim_canonicalize then
  36. rspamd_logger.errx(rspamd_config, "cannot enable arc plugin: dkim is disabled")
  37. return
  38. end
  39. local arc_symbols = {
  40. allow = 'ARC_ALLOW',
  41. invalid = 'ARC_INVALID',
  42. dnsfail = 'ARC_DNSFAIL',
  43. na = 'ARC_NA',
  44. reject = 'ARC_REJECT',
  45. }
  46. local settings = {
  47. allow_envfrom_empty = true,
  48. allow_hdrfrom_mismatch = false,
  49. allow_hdrfrom_mismatch_local = false,
  50. allow_hdrfrom_mismatch_sign_networks = false,
  51. allow_hdrfrom_multiple = false,
  52. allow_username_mismatch = false,
  53. sign_authenticated = true,
  54. domain = {},
  55. path = string.format('%s/%s/%s', rspamd_paths['DBDIR'], 'arc', '$domain.$selector.key'),
  56. sign_local = true,
  57. selector = 'arc',
  58. sign_symbol = 'ARC_SIGNED',
  59. try_fallback = true,
  60. use_domain = 'header',
  61. use_esld = true,
  62. use_redis = false,
  63. key_prefix = 'arc_keys', -- default hash name
  64. reuse_auth_results = false, -- Reuse the existing authentication results
  65. whitelisted_signers_map = nil, -- Trusted signers domains
  66. allowed_ids = nil, -- Allowed settings id
  67. forbidden_ids = nil, -- Banned settings id
  68. }
  69. -- To match normal AR
  70. local ar_settings = auth_results.default_settings
  71. local function parse_arc_header(hdr, target)
  72. -- Split elements by ';' and trim spaces
  73. local arr = fun.totable(fun.map(
  74. function(val)
  75. return fun.totable(fun.map(lua_util.rspamd_str_trim,
  76. fun.filter(function(v) return v and #v > 0 end,
  77. lua_util.rspamd_str_split(val.decoded, ';'))))
  78. end, hdr
  79. ))
  80. -- Now we have two tables in format:
  81. -- [sigs] -> [{sig1_elts}, {sig2_elts}...]
  82. for i,elts in ipairs(arr) do
  83. if not target[i] then target[i] = {} end
  84. -- Split by kv pair, like k=v
  85. fun.each(function(v)
  86. if v[1] and v[2] then
  87. target[i][v[1]] = v[2]
  88. end
  89. end, fun.map(function(elt)
  90. return lua_util.rspamd_str_split(elt, '=')
  91. end, elts))
  92. target[i].header = hdr[i].decoded
  93. target[i].raw_header = hdr[i].value
  94. end
  95. -- sort by i= attribute
  96. table.sort(target, function(a, b)
  97. return (a.i or 0) < (b.i or 0)
  98. end)
  99. end
  100. local function arc_validate_seals(task, seals, sigs, seal_headers, sig_headers)
  101. local fail_reason
  102. for i = 1,#seals do
  103. if (sigs[i].i or 0) ~= i then
  104. fail_reason = string.format('bad i for signature: %d, expected %d; d=%s',
  105. sigs[i].i, i, sigs[i].d)
  106. rspamd_logger.infox(task, fail_reason)
  107. task:insert_result(arc_symbols['invalid'], 1.0, fail_reason)
  108. return false,fail_reason
  109. end
  110. if (seals[i].i or 0) ~= i then
  111. fail_reason = string.format('bad i for seal: %d, expected %d; d=%s',
  112. seals[i].i, i, seals[i].d)
  113. rspamd_logger.infox(task, fail_reason)
  114. task:insert_result(arc_symbols['invalid'], 1.0,fail_reason)
  115. return false,fail_reason
  116. end
  117. if not seals[i].cv then
  118. fail_reason = string.format('no cv on i=%d', i)
  119. task:insert_result(arc_symbols['invalid'], 1.0, fail_reason)
  120. return false,fail_reason
  121. end
  122. if i == 1 then
  123. -- We need to ensure that cv of seal is equal to 'none'
  124. if seals[i].cv ~= 'none' then
  125. fail_reason = 'cv is not "none" for i=1'
  126. task:insert_result(arc_symbols['invalid'], 1.0, fail_reason)
  127. return false,fail_reason
  128. end
  129. else
  130. if seals[i].cv ~= 'pass' then
  131. fail_reason = string.format('cv is %s on i=%d', seals[i].cv, i)
  132. task:insert_result(arc_symbols['reject'], 1.0, fail_reason)
  133. return true,fail_reason
  134. end
  135. end
  136. end
  137. return true,nil
  138. end
  139. local function arc_callback(task)
  140. local arc_sig_headers = task:get_header_full('ARC-Message-Signature')
  141. local arc_seal_headers = task:get_header_full('ARC-Seal')
  142. if not arc_sig_headers or not arc_seal_headers then
  143. task:insert_result(arc_symbols['na'], 1.0)
  144. return
  145. end
  146. if #arc_sig_headers ~= #arc_seal_headers then
  147. -- We mandate that count of seals is equal to count of signatures
  148. rspamd_logger.infox(task, 'number of seals (%s) is not equal to number of signatures (%s)',
  149. #arc_seal_headers, #arc_sig_headers)
  150. task:insert_result(arc_symbols['invalid'], 1.0, 'invalid count of seals and signatures')
  151. return
  152. end
  153. local cbdata = {
  154. seals = {},
  155. sigs = {},
  156. checked = 0,
  157. res = 'success',
  158. errors = {},
  159. allowed_by_trusted = false
  160. }
  161. parse_arc_header(arc_seal_headers, cbdata.seals)
  162. parse_arc_header(arc_sig_headers, cbdata.sigs)
  163. -- Fix i type
  164. fun.each(function(hdr)
  165. hdr.i = tonumber(hdr.i) or 0
  166. end, cbdata.seals)
  167. fun.each(function(hdr)
  168. hdr.i = tonumber(hdr.i) or 0
  169. end, cbdata.sigs)
  170. -- Now we need to sort elements according to their [i] value
  171. table.sort(cbdata.seals, function(e1, e2)
  172. return (e1.i or 0) < (e2.i or 0)
  173. end)
  174. table.sort(cbdata.sigs, function(e1, e2)
  175. return (e1.i or 0) < (e2.i or 0)
  176. end)
  177. lua_util.debugm(N, task, 'got %s arc sections', #cbdata.seals)
  178. -- Now check sanity of what we have
  179. local valid,validation_error = arc_validate_seals(task, cbdata.seals, cbdata.sigs,
  180. arc_seal_headers, arc_sig_headers)
  181. if not valid then
  182. task:cache_set('arc-failure', validation_error)
  183. return
  184. end
  185. task:cache_set('arc-sigs', cbdata.sigs)
  186. task:cache_set('arc-seals', cbdata.seals)
  187. if validation_error then
  188. -- ARC rejection but no strong failure for signing
  189. return
  190. end
  191. local function gen_arc_seal_cb(sig)
  192. return function (_, res, err, domain)
  193. cbdata.checked = cbdata.checked + 1
  194. lua_util.debugm(N, task, 'checked arc seal: %s(%s), %s processed',
  195. res, err, cbdata.checked)
  196. if not res then
  197. cbdata.res = 'fail'
  198. if err and domain then
  199. table.insert(cbdata.errors, string.format('sig:%s:%s', domain, err))
  200. end
  201. end
  202. if settings.whitelisted_signers_map and cbdata.res == 'success' then
  203. if settings.whitelisted_signers_map:get_key(sig.d) then
  204. -- Whitelisted signer has been found in a valid chain
  205. task:insert_result(arc_symbols.trusted_allow, 1.0,
  206. string.format('%s:s=%s:i=%d', domain, sig.s, cbdata.checked))
  207. end
  208. end
  209. if cbdata.checked == #arc_sig_headers then
  210. if cbdata.res == 'success' then
  211. local arc_allow_result = string.format('%s:s=%s:i=%d',
  212. domain, sig.s, cbdata.checked)
  213. task:insert_result(arc_symbols.allow, 1.0, arc_allow_result)
  214. task:cache_set('arc-allow', arc_allow_result)
  215. else
  216. task:insert_result(arc_symbols.reject, 1.0,
  217. rspamd_logger.slog('seal check failed: %s, %s', cbdata.res,
  218. cbdata.errors))
  219. end
  220. end
  221. end
  222. end
  223. local function arc_signature_cb(_, res, err, domain)
  224. lua_util.debugm(N, task, 'checked arc signature %s: %s(%s), %s processed',
  225. domain, res, err, cbdata.checked)
  226. if not res then
  227. cbdata.res = 'fail'
  228. if err and domain then
  229. table.insert(cbdata.errors, string.format('sig:%s:%s', domain, err))
  230. end
  231. end
  232. if cbdata.res == 'success' then
  233. -- Verify seals
  234. cbdata.checked = 0
  235. fun.each(
  236. function(sig)
  237. local ret, lerr = dkim_verify(task, sig.header, gen_arc_seal_cb(sig), 'arc-seal')
  238. if not ret then
  239. cbdata.res = 'fail'
  240. table.insert(cbdata.errors, string.format('seal:%s:s=%s:i=%s:%s',
  241. sig.d or '', sig.s or '', sig.i or '', lerr))
  242. cbdata.checked = cbdata.checked + 1
  243. lua_util.debugm(N, task, 'checked arc seal %s: %s(%s), %s processed',
  244. sig.d, ret, lerr, cbdata.checked)
  245. end
  246. end, cbdata.seals)
  247. else
  248. task:insert_result(arc_symbols['reject'], 1.0,
  249. rspamd_logger.slog('signature check failed: %s, %s', cbdata.res,
  250. cbdata.errors))
  251. end
  252. end
  253. --[[
  254. 1. Collect all ARC Sets currently attached to the message. If there
  255. are none, the Chain Validation Status is "none" and the algorithm
  256. stops here. The maximum number of ARC Sets that can be attached
  257. to a message is 50. If more than the maximum number exist the
  258. Chain Validation Status is "fail" and the algorithm stops here.
  259. In the following algorithm, the maximum ARC instance value is
  260. referred to as "N".
  261. 2. If the Chain Validation Status of the highest instance value ARC
  262. Set is "fail", then the Chain Validation status is "fail" and the
  263. algorithm stops here.
  264. 3. Validate the structure of the Authenticated Received Chain. A
  265. valid ARC has the following conditions:
  266. 1. Each ARC Set MUST contain exactly one each of the three ARC
  267. header fields (AAR, AMS, and AS).
  268. 2. The instance values of the ARC Sets MUST form a continuous
  269. sequence from 1..N with no gaps or repetition.
  270. 3. The "cv" value for all ARC-Seal header fields must be non-
  271. failing. For instance values > 1, the value must be "pass".
  272. For instance value = 1, the value must be "none".
  273. * If any of these conditions are not met, the Chain Validation
  274. Status is "fail" and the algorithm stops here.
  275. 4. Validate the AMS with the greatest instance value (most recent).
  276. If validation fails, then the Chain Validation Status is "fail"
  277. and the algorithm stops here.
  278. 5 - 7. Optional, not implemented
  279. 8. Validate each AS beginning with the greatest instance value and
  280. proceeding in decreasing order to the AS with the instance value
  281. of 1. If any AS fails to validate, the Chain Validation Status
  282. is "fail" and the algorithm stops here.
  283. 9. If the algorithm reaches this step, then the Chain Validation
  284. Status is "pass", and the algorithm is complete.
  285. ]]--
  286. local processed = 0
  287. local sig = cbdata.sigs[#cbdata.sigs] -- last AMS
  288. local ret,err = dkim_verify(task, sig.header, arc_signature_cb, 'arc-sign')
  289. if not ret then
  290. cbdata.res = 'fail'
  291. table.insert(cbdata.errors, string.format('sig:%s:%s', sig.d or '', err))
  292. else
  293. processed = processed + 1
  294. lua_util.debugm(N, task, 'processed arc signature %s[%s]: %s(%s), %s processed',
  295. sig.d, sig.i, ret, err, cbdata.checked)
  296. end
  297. if processed == 0 then
  298. task:insert_result(arc_symbols['reject'], 1.0,
  299. rspamd_logger.slog('cannot verify %s of %s signatures: %s',
  300. #arc_sig_headers - processed, #arc_sig_headers, cbdata.errors))
  301. end
  302. end
  303. local opts = rspamd_config:get_all_opt('arc')
  304. if not opts or type(opts) ~= 'table' then
  305. return
  306. end
  307. if opts['symbols'] then
  308. for k,_ in pairs(arc_symbols) do
  309. if opts['symbols'][k] then
  310. arc_symbols[k] = opts['symbols'][k]
  311. end
  312. end
  313. end
  314. local id = rspamd_config:register_symbol({
  315. name = 'ARC_CALLBACK',
  316. type = 'callback',
  317. group = 'policies',
  318. groups = {'arc'},
  319. callback = arc_callback
  320. })
  321. rspamd_config:register_symbol({
  322. name = arc_symbols['allow'],
  323. parent = id,
  324. type = 'virtual',
  325. score = -1.0,
  326. group = 'policies',
  327. groups = {'arc'},
  328. })
  329. rspamd_config:register_symbol({
  330. name = arc_symbols['reject'],
  331. parent = id,
  332. type = 'virtual',
  333. score = 2.0,
  334. group = 'policies',
  335. groups = {'arc'},
  336. })
  337. rspamd_config:register_symbol({
  338. name = arc_symbols['invalid'],
  339. parent = id,
  340. type = 'virtual',
  341. score = 1.0,
  342. group = 'policies',
  343. groups = {'arc'},
  344. })
  345. rspamd_config:register_symbol({
  346. name = arc_symbols['dnsfail'],
  347. parent = id,
  348. type = 'virtual',
  349. score = 0.0,
  350. group = 'policies',
  351. groups = {'arc'},
  352. })
  353. rspamd_config:register_symbol({
  354. name = arc_symbols['na'],
  355. parent = id,
  356. type = 'virtual',
  357. score = 0.0,
  358. group = 'policies',
  359. groups = {'arc'},
  360. })
  361. if settings.whitelisted_signers_map then
  362. local lua_maps = require "lua_maps"
  363. settings.whitelisted_signers_map = lua_maps.map_add_from_ucl(settings.whitelisted_signers_map,
  364. 'set',
  365. 'ARC trusted signers domains')
  366. if settings.whitelisted_signers_map then
  367. arc_symbols.trusted_allow = arc_symbols.trusted_allow or 'ARC_ALLOW_TRUSTED'
  368. rspamd_config:register_symbol({
  369. name = arc_symbols.trusted_allow,
  370. parent = id,
  371. type = 'virtual',
  372. score = -2.0,
  373. group = 'policies',
  374. groups = {'arc'},
  375. })
  376. end
  377. end
  378. rspamd_config:register_dependency('ARC_CALLBACK', 'SPF_CHECK')
  379. rspamd_config:register_dependency('ARC_CALLBACK', 'DKIM_CHECK')
  380. local function arc_sign_seal(task, params, header)
  381. local arc_sigs = task:cache_get('arc-sigs')
  382. local arc_seals = task:cache_get('arc-seals')
  383. local arc_auth_results = task:get_header_full('ARC-Authentication-Results') or {}
  384. local cur_auth_results
  385. local privkey
  386. if params.rawkey then
  387. -- Distinguish between pem and base64
  388. if string.match(params.rawkey, '^-----BEGIN') then
  389. privkey = rspamd_rsa_privkey.load_pem(params.rawkey)
  390. else
  391. privkey = rspamd_rsa_privkey.load_base64(params.rawkey)
  392. end
  393. elseif params.key then
  394. privkey = rspamd_rsa_privkey.load_file(params.key)
  395. end
  396. if not privkey then
  397. rspamd_logger.errx(task, 'cannot load private key for signing')
  398. return
  399. end
  400. if settings.reuse_auth_results then
  401. local ar_header = task:get_header('Authentication-Results')
  402. if ar_header then
  403. rspamd_logger.debugm(N, task, 'reuse authentication results header for ARC')
  404. cur_auth_results = ar_header
  405. else
  406. rspamd_logger.debugm(N, task, 'cannot reuse authentication results, header is missing')
  407. cur_auth_results = auth_results.gen_auth_results(task, ar_settings) or ''
  408. end
  409. else
  410. cur_auth_results = auth_results.gen_auth_results(task, ar_settings) or ''
  411. end
  412. local sha_ctx = hash.create_specific('sha256')
  413. -- Update using previous seals + sigs + AAR
  414. local cur_idx = 1
  415. if arc_seals then
  416. cur_idx = #arc_seals + 1
  417. for i = (cur_idx - 1), 1, (-1) do
  418. if arc_auth_results[i] then
  419. local s = dkim_canonicalize('ARC-Authentication-Results',
  420. arc_auth_results[i].value)
  421. sha_ctx:update(s)
  422. lua_util.debugm(N, task, 'update signature with header: %s', s)
  423. end
  424. if arc_sigs[i] then
  425. local s = dkim_canonicalize('ARC-Message-Signature',
  426. arc_sigs[i].raw_header)
  427. sha_ctx:update(s)
  428. lua_util.debugm(N, task, 'update signature with header: %s', s)
  429. end
  430. if arc_seals[i] then
  431. local s = dkim_canonicalize('ARC-Seal', arc_seals[i].raw_header)
  432. sha_ctx:update(s)
  433. lua_util.debugm(N, task, 'update signature with header: %s', s)
  434. end
  435. end
  436. end
  437. header = lua_util.fold_header(task,
  438. 'ARC-Message-Signature',
  439. header)
  440. cur_auth_results = string.format('i=%d; %s', cur_idx, cur_auth_results)
  441. cur_auth_results = lua_util.fold_header(task,
  442. 'ARC-Authentication-Results',
  443. cur_auth_results, ';')
  444. local s = dkim_canonicalize('ARC-Authentication-Results',
  445. cur_auth_results)
  446. sha_ctx:update(s)
  447. lua_util.debugm(N, task, 'update signature with header: %s', s)
  448. s = dkim_canonicalize('ARC-Message-Signature', header)
  449. sha_ctx:update(s)
  450. lua_util.debugm(N, task, 'update signature with header: %s', s)
  451. local cur_arc_seal = string.format('i=%d; s=%s; d=%s; t=%d; a=rsa-sha256; cv=%s; b=',
  452. cur_idx,
  453. params.selector,
  454. params.domain,
  455. math.floor(rspamd_util.get_time()), params.arc_cv)
  456. s = string.format('%s:%s', 'arc-seal', cur_arc_seal)
  457. sha_ctx:update(s)
  458. lua_util.debugm(N, task, 'initial update signature with header: %s', s)
  459. local nl_type
  460. if task:has_flag("milter") then
  461. nl_type = "lf"
  462. else
  463. nl_type = task:get_newlines_type()
  464. end
  465. local sig = rspamd_rsa.sign_memory(privkey, sha_ctx:bin())
  466. cur_arc_seal = string.format('%s%s', cur_arc_seal,
  467. sig:base64(70, nl_type))
  468. lua_mime.modify_headers(task, {
  469. add = {
  470. ['ARC-Authentication-Results'] = {order = 1, value = cur_auth_results},
  471. ['ARC-Message-Signature'] = {order = 1, value = header},
  472. ['ARC-Seal'] = {order = 1, value = lua_util.fold_header(task,
  473. 'ARC-Seal', cur_arc_seal) }
  474. },
  475. -- RFC requires a strict order for these headers to be inserted
  476. order = {'ARC-Authentication-Results', 'ARC-Message-Signature', 'ARC-Seal'},
  477. })
  478. task:insert_result(settings.sign_symbol, 1.0,
  479. string.format('%s:s=%s:i=%d', params.domain, params.selector, cur_idx))
  480. end
  481. local function prepare_arc_selector(task, sel)
  482. local arc_seals = task:cache_get('arc-seals')
  483. if not arc_seals then
  484. -- Check if our arc is broken
  485. local failure_reason = task:cache_get('arc-failure')
  486. if failure_reason then
  487. rspamd_logger.infox(task, 'skip ARC as the existing chain is broken: %s', failure_reason)
  488. return false
  489. end
  490. end
  491. sel.arc_cv = 'none'
  492. sel.arc_idx = 1
  493. sel.no_cache = true
  494. sel.sign_type = 'arc-sign'
  495. if arc_seals then
  496. sel.arc_idx = #arc_seals + 1
  497. local function default_arc_cv()
  498. if task:cache_get('arc-allow') then
  499. sel.arc_cv = 'pass'
  500. else
  501. sel.arc_cv = 'fail'
  502. end
  503. end
  504. if settings.reuse_auth_results then
  505. local ar_header = task:get_header('Authentication-Results')
  506. if ar_header then
  507. local arc_match = string.match(ar_header, 'arc=(%w+)')
  508. if arc_match then
  509. if arc_match == 'none' or arc_match == 'pass' then
  510. -- none should be converted to `pass`
  511. sel.arc_cv = 'pass'
  512. else
  513. sel.arc_cv = 'fail'
  514. end
  515. else
  516. default_arc_cv()
  517. end
  518. else
  519. -- Cannot reuse, use normal path
  520. default_arc_cv()
  521. end
  522. else
  523. default_arc_cv()
  524. end
  525. end
  526. return true
  527. end
  528. local function do_sign(task, sign_params)
  529. if sign_params.alg and sign_params.alg ~= 'rsa' then
  530. -- No support for ed25519 keys
  531. return
  532. end
  533. if not prepare_arc_selector(task, sign_params) then
  534. -- Broken arc
  535. return
  536. end
  537. if settings.check_pubkey then
  538. local resolve_name = sign_params.selector .. "._domainkey." .. sign_params.domain
  539. task:get_resolver():resolve_txt({
  540. task = task,
  541. name = resolve_name,
  542. callback = function(_, _, results, err)
  543. if not err and results and results[1] then
  544. sign_params.pubkey = results[1]
  545. sign_params.strict_pubkey_check = not settings.allow_pubkey_mismatch
  546. elseif not settings.allow_pubkey_mismatch then
  547. rspamd_logger.errx('public key for domain %s/%s is not found: %s, skip signing',
  548. sign_params.domain, sign_params.selector, err)
  549. return
  550. else
  551. rspamd_logger.infox('public key for domain %s/%s is not found: %s',
  552. sign_params.domain, sign_params.selector, err)
  553. end
  554. local dret, hdr = dkim_sign(task, sign_params)
  555. if dret then
  556. arc_sign_seal(task, sign_params, hdr)
  557. end
  558. end,
  559. forced = true
  560. })
  561. else
  562. local dret, hdr = dkim_sign(task, sign_params)
  563. if dret then
  564. arc_sign_seal(task, sign_params, hdr)
  565. end
  566. end
  567. end
  568. local function sign_error(task, msg)
  569. rspamd_logger.errx(task, 'signing failure: %s', msg)
  570. end
  571. local function arc_signing_cb(task)
  572. local ret, selectors = dkim_sign_tools.prepare_dkim_signing(N, task, settings)
  573. if not ret then
  574. return
  575. end
  576. if settings.use_redis then
  577. dkim_sign_tools.sign_using_redis(N, task, settings, selectors, do_sign, sign_error)
  578. else
  579. if selectors.vault then
  580. dkim_sign_tools.sign_using_vault(N, task, settings, selectors, do_sign, sign_error)
  581. else
  582. -- TODO: no support for multiple sigs
  583. local cur_selector = selectors[1]
  584. prepare_arc_selector(task, cur_selector)
  585. if ((cur_selector.key or cur_selector.rawkey) and cur_selector.selector) then
  586. if cur_selector.key then
  587. cur_selector.key = lua_util.template(cur_selector.key, {
  588. domain = cur_selector.domain,
  589. selector = cur_selector.selector
  590. })
  591. local exists,err = rspamd_util.file_exists(cur_selector.key)
  592. if not exists then
  593. if err and err == 'No such file or directory' then
  594. lua_util.debugm(N, task, 'cannot read key from %s: %s', cur_selector.key, err)
  595. else
  596. rspamd_logger.warnx(task, 'cannot read key from %s: %s', cur_selector.key, err)
  597. end
  598. return false
  599. end
  600. end
  601. do_sign(task, cur_selector)
  602. else
  603. rspamd_logger.infox(task, 'key path or dkim selector unconfigured; no signing')
  604. return false
  605. end
  606. end
  607. end
  608. end
  609. dkim_sign_tools.process_signing_settings(N, settings, opts)
  610. if not dkim_sign_tools.validate_signing_settings(settings) then
  611. rspamd_logger.infox(rspamd_config, 'mandatory parameters missing, disable arc signing')
  612. return
  613. end
  614. local ar_opts = rspamd_config:get_all_opt('milter_headers')
  615. if ar_opts and ar_opts.routines then
  616. local routines = ar_opts.routines
  617. if routines['authentication-results'] then
  618. ar_settings = lua_util.override_defaults(ar_settings,
  619. routines['authentication-results'])
  620. end
  621. end
  622. if settings.use_redis then
  623. redis_params = rspamd_parse_redis_server('arc')
  624. if not redis_params then
  625. rspamd_logger.errx(rspamd_config, 'no servers are specified, '..
  626. 'but module is configured to load keys from redis, disable arc signing')
  627. return
  628. end
  629. settings.redis_params = redis_params
  630. end
  631. local sym_reg_tbl = {
  632. name = settings['sign_symbol'],
  633. callback = arc_signing_cb,
  634. groups = {"policies", "arc"},
  635. score = 0.0,
  636. }
  637. if type(settings.allowed_ids) == 'table' then
  638. sym_reg_tbl.allowed_ids = settings.allowed_ids
  639. end
  640. if type(settings.forbidden_ids) == 'table' then
  641. sym_reg_tbl.forbidden_ids = settings.forbidden_ids
  642. end
  643. rspamd_config:register_symbol(sym_reg_tbl)
  644. -- Do not sign unless checked
  645. rspamd_config:register_dependency(settings['sign_symbol'], 'ARC_CALLBACK')
  646. -- We need to check dmarc before signing as we have to produce valid AAR header
  647. -- see #3613
  648. rspamd_config:register_dependency(settings['sign_symbol'], 'DMARC_CALLBACK')