You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

lua_dkim_tools.lua 22KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. --[[
  2. Copyright (c) 2016, Andrew Lewis <nerf@judo.za.org>
  3. Copyright (c) 2017, Vsevolod Stakhov <vsevolod@highsecure.ru>
  4. Licensed under the Apache License, Version 2.0 (the "License");
  5. you may not use this file except in compliance with the License.
  6. You may obtain a copy of the License at
  7. http://www.apache.org/licenses/LICENSE-2.0
  8. Unless required by applicable law or agreed to in writing, software
  9. distributed under the License is distributed on an "AS IS" BASIS,
  10. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  11. See the License for the specific language governing permissions and
  12. limitations under the License.
  13. ]]--
  14. local exports = {}
  15. local E = {}
  16. local lua_util = require "lua_util"
  17. local rspamd_util = require "rspamd_util"
  18. local logger = require "rspamd_logger"
  19. local fun = require "fun"
  20. local function check_violation(N, task, domain)
  21. -- Check for DKIM_REJECT
  22. local sym_check = 'R_DKIM_REJECT'
  23. if N == 'arc' then sym_check = 'ARC_REJECT' end
  24. if task:has_symbol(sym_check) then
  25. local sym = task:get_symbol(sym_check)
  26. logger.infox(task, 'skip signing for %s: violation %s found: %s',
  27. domain, sym_check, sym.options)
  28. return false
  29. end
  30. return true
  31. end
  32. local function insert_or_update_prop(N, task, p, prop, origin, data)
  33. if #p == 0 then
  34. local k = {}
  35. k[prop] = data
  36. table.insert(p, k)
  37. lua_util.debugm(N, task, 'add %s "%s" using %s', prop, data, origin)
  38. else
  39. for _, k in ipairs(p) do
  40. if not k[prop] then
  41. k[prop] = data
  42. lua_util.debugm(N, task, 'set %s to "%s" using %s', prop, data, origin)
  43. end
  44. end
  45. end
  46. end
  47. local function get_mempool_selectors(N, task)
  48. local p = {}
  49. local key_var = "dkim_key"
  50. local selector_var = "dkim_selector"
  51. if N == "arc" then
  52. key_var = "arc_key"
  53. selector_var = "arc_selector"
  54. end
  55. p.key = task:get_mempool():get_variable(key_var)
  56. p.selector = task:get_mempool():get_variable(selector_var)
  57. if (not p.key or not p.selector) then
  58. return false, {}
  59. end
  60. lua_util.debugm(N, task, 'override selector and key to %s:%s', p.key, p.selector)
  61. return true, p
  62. end
  63. local function parse_dkim_http_headers(N, task, settings)
  64. -- Configure headers
  65. local headers = {
  66. sign_header = settings.http_sign_header or "PerformDkimSign",
  67. sign_on_reject_header = settings.http_sign_on_reject_header_header or 'SignOnAuthFailed',
  68. domain_header = settings.http_domain_header or 'DkimDomain',
  69. selector_header = settings.http_selector_header or 'DkimSelector',
  70. key_header = settings.http_key_header or 'DkimPrivateKey'
  71. }
  72. if task:get_request_header(headers.sign_header) then
  73. local domain = task:get_request_header(headers.domain_header)
  74. local selector = task:get_request_header(headers.selector_header)
  75. local key = task:get_request_header(headers.key_header)
  76. if not (domain and selector and key) then
  77. logger.errx(task, 'missing required headers to sign email')
  78. return false,{}
  79. end
  80. -- Now check if we need to check the existing auth
  81. local hdr = task:get_request_header(headers.sign_on_reject_header)
  82. if not hdr or tostring(hdr) == '0' or tostring(hdr) == 'false' then
  83. if not check_violation(N, task, domain, selector) then
  84. return false, {}
  85. end
  86. end
  87. local p = {}
  88. local k = {
  89. domain = tostring(domain),
  90. rawkey = tostring(key),
  91. selector = tostring(selector),
  92. }
  93. table.insert(p, k)
  94. return true, p
  95. end
  96. lua_util.debugm(N, task, 'no sign header %s', headers.sign_header)
  97. return false,{}
  98. end
  99. local function prepare_dkim_signing(N, task, settings)
  100. local is_local, is_sign_networks, is_authed
  101. if settings.use_http_headers then
  102. local res,tbl = parse_dkim_http_headers(N, task, settings)
  103. if not res then
  104. if not settings.allow_headers_fallback then
  105. return res,{}
  106. else
  107. lua_util.debugm(N, task, 'failed to read http headers, fallback to normal schema')
  108. end
  109. else
  110. return res,tbl
  111. end
  112. end
  113. if settings.sign_condition and type(settings.sign_condition) == 'function' then
  114. -- Use sign condition only
  115. local ret = settings.sign_condition(task)
  116. if not ret then
  117. return false,{}
  118. end
  119. if ret[1] then
  120. return true,ret
  121. else
  122. return true,{ret}
  123. end
  124. end
  125. local auser = task:get_user()
  126. local ip = task:get_from_ip()
  127. if ip and ip:is_local() then
  128. is_local = true
  129. end
  130. if settings.sign_authenticated and auser then
  131. lua_util.debugm(N, task, 'user is authenticated')
  132. is_authed = true
  133. elseif (settings.sign_networks and settings.sign_networks:get_key(ip)) then
  134. is_sign_networks = true
  135. lua_util.debugm(N, task, 'mail is from address in sign_networks')
  136. elseif settings.sign_local and is_local then
  137. lua_util.debugm(N, task, 'mail is from local address')
  138. elseif settings.sign_inbound and not is_local and not auser then
  139. lua_util.debugm(N, task, 'mail was sent to us')
  140. else
  141. lua_util.debugm(N, task, 'mail is ineligible for signing')
  142. return false,{}
  143. end
  144. local efrom = task:get_from('smtp')
  145. local empty_envelope = false
  146. if #(((efrom or E)[1] or E).addr or '') == 0 then
  147. if not settings.allow_envfrom_empty then
  148. lua_util.debugm(N, task, 'empty envelope from not allowed')
  149. return false,{}
  150. else
  151. empty_envelope = true
  152. end
  153. end
  154. local hfrom = task:get_from('mime')
  155. if not settings.allow_hdrfrom_multiple and (hfrom or E)[2] then
  156. lua_util.debugm(N, task, 'multiple header from not allowed')
  157. return false,{}
  158. end
  159. local eto = task:get_recipients(0)
  160. local dkim_domain
  161. local hdom = ((hfrom or E)[1] or E).domain
  162. local edom = ((efrom or E)[1] or E).domain
  163. local tdom = ((eto or E)[1] or E).domain
  164. local udom = string.match(auser or '', '.*@(.*)')
  165. local function get_dkim_domain(dtype)
  166. if settings[dtype] == 'header' then
  167. return hdom
  168. elseif settings[dtype] == 'envelope' then
  169. return edom
  170. elseif settings[dtype] == 'auth' then
  171. return udom
  172. elseif settings[dtype] == 'recipient' then
  173. return tdom
  174. else
  175. return settings[dtype]:lower()
  176. end
  177. end
  178. local function is_skip_sign()
  179. return not (settings.sign_networks and is_sign_networks) and
  180. not (settings.sign_authenticated and is_authed) and
  181. not (settings.sign_local and is_local)
  182. end
  183. if hdom then
  184. hdom = hdom:lower()
  185. end
  186. if edom then
  187. edom = edom:lower()
  188. end
  189. if udom then
  190. udom = udom:lower()
  191. end
  192. if tdom then
  193. tdom = tdom:lower()
  194. end
  195. if settings.signing_table and (settings.key_table or settings.use_vault) then
  196. -- OpenDKIM style
  197. if is_skip_sign() then
  198. lua_util.debugm(N, task,
  199. 'skip signing: is_sign_network: %s, is_authed: %s, is_local: %s',
  200. is_sign_networks, is_authed, is_local)
  201. return false,{}
  202. end
  203. if not hfrom or not hfrom[1] or not hfrom[1].addr then
  204. lua_util.debugm(N, task,
  205. 'signing_table: cannot get data when no header from is presented')
  206. return false,{}
  207. end
  208. local sign_entry = settings.signing_table:get_key(hfrom[1].addr)
  209. if sign_entry then
  210. -- Check opendkim style entries
  211. lua_util.debugm(N, task,
  212. 'signing_table: found entry for %s: %s', hfrom[1].addr, sign_entry)
  213. if sign_entry == '%' then
  214. sign_entry = hdom
  215. end
  216. if settings.key_table then
  217. -- Now search in key table
  218. local key_entry = settings.key_table:get_key(sign_entry)
  219. if key_entry then
  220. local parts = lua_util.str_split(key_entry, ':')
  221. if #parts == 2 then
  222. -- domain + key
  223. local selector = settings.selector
  224. if not selector then
  225. logger.errx(task, 'no selector defined for sign_entry %s, key_entry %s',
  226. sign_entry, key_entry)
  227. return false,{}
  228. end
  229. local res = {
  230. selector = selector,
  231. domain = parts[1]:gsub('%%', hdom)
  232. }
  233. local st = parts[2]:sub(1, 2)
  234. if st:sub(1, 1) == '/' or st == './' or st == '..' then
  235. res.key = parts[2]:gsub('%%', hdom)
  236. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, key file=%s',
  237. hdom, selector, res.domain, res.key)
  238. else
  239. res.rawkey = parts[2] -- No sanity check here
  240. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, raw key used',
  241. hdom, selector, res.domain)
  242. end
  243. return true,{res}
  244. elseif #parts == 3 then
  245. -- domain, selector, key
  246. local selector = parts[2]
  247. local res = {
  248. selector = selector,
  249. domain = parts[1]:gsub('%%', hdom)
  250. }
  251. local st = parts[3]:sub(1, 2)
  252. if st:sub(1, 1) == '/' or st == './' or st == '..' then
  253. res.key = parts[3]:gsub('%%', hdom)
  254. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, key file=%s',
  255. hdom, selector, res.domain, res.key)
  256. else
  257. res.rawkey = parts[3] -- No sanity check here
  258. lua_util.debugm(N, task, 'perform dkim signing for %s, selector=%s, domain=%s, raw key used',
  259. hdom, selector, res.domain)
  260. end
  261. return true,{res}
  262. else
  263. logger.errx(task, 'invalid key entry for sign entry %s: %s; when signing %s domain',
  264. sign_entry, key_entry, hdom)
  265. return false,{}
  266. end
  267. elseif settings.use_vault then
  268. -- Sign table is presented, the rest is covered by vault
  269. lua_util.debugm(N, task, 'check vault for %s, by sign entry %s, key entry is missing',
  270. hdom, sign_entry)
  271. return true, {
  272. domain = sign_entry,
  273. vault = true
  274. }
  275. else
  276. logger.errx(task, 'missing key entry for sign entry %s; when signing %s domain',
  277. sign_entry, hdom)
  278. return false,{}
  279. end
  280. else
  281. logger.errx(task, 'cannot get key entry for signing entry %s, when signing %s domain',
  282. sign_entry, hdom)
  283. return false,{}
  284. end
  285. else
  286. lua_util.debugm(N, task,
  287. 'signing_table: no entry for %s', hfrom[1].addr)
  288. return false,{}
  289. end
  290. else
  291. if settings.use_domain_sign_networks and is_sign_networks then
  292. dkim_domain = get_dkim_domain('use_domain_sign_networks')
  293. lua_util.debugm(N, task,
  294. 'sign_networks: use domain(%s) for signature: %s',
  295. settings.use_domain_sign_networks, dkim_domain)
  296. elseif settings.use_domain_sign_local and is_local then
  297. dkim_domain = get_dkim_domain('use_domain_sign_local')
  298. lua_util.debugm(N, task, 'local: use domain(%s) for signature: %s',
  299. settings.use_domain_sign_local, dkim_domain)
  300. elseif settings.use_domain_sign_inbound and not is_local and not auser then
  301. dkim_domain = get_dkim_domain('use_domain_sign_inbound')
  302. lua_util.debugm(N, task, 'inbound: use domain(%s) for signature: %s',
  303. settings.use_domain_sign_inbound, dkim_domain)
  304. elseif settings.use_domain_custom then
  305. if type(settings.use_domain_custom) == 'string' then
  306. -- Load custom function
  307. local loadstring = loadstring or load
  308. local ret, res_or_err = pcall(loadstring(settings.use_domain_custom))
  309. if ret then
  310. if type(res_or_err) == 'function' then
  311. settings.use_domain_custom = res_or_err
  312. dkim_domain = settings.use_domain_custom(task)
  313. lua_util.debugm(N, task, 'use custom domain for signing: %s',
  314. dkim_domain)
  315. else
  316. logger.errx(task, 'cannot load dkim domain custom script: invalid type: %s, expected function',
  317. type(res_or_err))
  318. settings.use_domain_custom = nil
  319. end
  320. else
  321. logger.errx(task, 'cannot load dkim domain custom script: %s', res_or_err)
  322. settings.use_domain_custom = nil
  323. end
  324. else
  325. dkim_domain = settings.use_domain_custom(task)
  326. lua_util.debugm(N, task, 'use custom domain for signing: %s',
  327. dkim_domain)
  328. end
  329. else
  330. dkim_domain = get_dkim_domain('use_domain')
  331. lua_util.debugm(N, task, 'use domain(%s) for signature: %s',
  332. settings.use_domain, dkim_domain)
  333. end
  334. end
  335. if not dkim_domain then
  336. lua_util.debugm(N, task, 'could not extract dkim domain')
  337. return false,{}
  338. end
  339. if settings.use_esld then
  340. dkim_domain = rspamd_util.get_tld(dkim_domain)
  341. if hdom then
  342. hdom = rspamd_util.get_tld(hdom)
  343. end
  344. if edom then
  345. edom = rspamd_util.get_tld(edom)
  346. end
  347. end
  348. lua_util.debugm(N, task, 'final DKIM domain: %s', dkim_domain)
  349. -- Sanity checks
  350. if edom and hdom and not settings.allow_hdrfrom_mismatch and hdom ~= edom then
  351. if settings.allow_hdrfrom_mismatch_local and is_local then
  352. lua_util.debugm(N, task, 'domain mismatch allowed for local IP: %1 != %2', hdom, edom)
  353. elseif settings.allow_hdrfrom_mismatch_sign_networks and is_sign_networks then
  354. lua_util.debugm(N, task, 'domain mismatch allowed for sign_networks: %1 != %2', hdom, edom)
  355. else
  356. if empty_envelope and hdom then
  357. lua_util.debugm(N, task, 'domain mismatch allowed for empty envelope: %1 != %2', hdom, edom)
  358. else
  359. lua_util.debugm(N, task, 'domain mismatch not allowed: %1 != %2', hdom, edom)
  360. return false,{}
  361. end
  362. end
  363. end
  364. if auser and not settings.allow_username_mismatch then
  365. if not udom then
  366. lua_util.debugm(N, task, 'couldnt find domain in username')
  367. return false,{}
  368. end
  369. if settings.use_esld then
  370. udom = rspamd_util.get_tld(udom)
  371. end
  372. if udom ~= dkim_domain then
  373. lua_util.debugm(N, task, 'user domain mismatch')
  374. return false,{}
  375. end
  376. end
  377. local p = {}
  378. if settings.use_vault then
  379. if settings.vault_domains then
  380. if settings.vault_domains:get_key(dkim_domain) then
  381. return true, {
  382. domain = dkim_domain,
  383. vault = true,
  384. }
  385. else
  386. lua_util.debugm(N, task, 'domain %s is not designated for vault',
  387. dkim_domain)
  388. return false,{}
  389. end
  390. else
  391. -- TODO: try every domain in the vault
  392. return true, {
  393. domain = dkim_domain,
  394. vault = true,
  395. }
  396. end
  397. end
  398. if settings.domain[dkim_domain] then
  399. -- support old style selector/paths
  400. if settings.domain[dkim_domain].selector or
  401. settings.domain[dkim_domain].path then
  402. local k = {}
  403. k.selector = settings.domain[dkim_domain].selector
  404. k.key = settings.domain[dkim_domain].path
  405. table.insert(p, k)
  406. end
  407. for _, s in ipairs((settings.domain[dkim_domain].selectors or {})) do
  408. lua_util.debugm(N, task, 'adding selector: %1', s)
  409. local k = {}
  410. k.selector = s.selector
  411. k.key = s.path
  412. table.insert(p, k)
  413. end
  414. end
  415. if #p == 0 then
  416. local ret, k = get_mempool_selectors(N, task)
  417. if ret then
  418. table.insert(p, k)
  419. lua_util.debugm(N, task, 'using mempool selector %s with key %s',
  420. k.selector, k.key)
  421. end
  422. end
  423. if settings.selector_map then
  424. local data = settings.selector_map:get_key(dkim_domain)
  425. if data then
  426. insert_or_update_prop(N, task, p, 'selector', 'selector_map', data)
  427. else
  428. lua_util.debugm(N, task, 'no selector in map for %s', dkim_domain)
  429. end
  430. end
  431. if settings.path_map then
  432. local data = settings.path_map:get_key(dkim_domain)
  433. if data then
  434. insert_or_update_prop(N, task, p, 'key', 'path_map', data)
  435. else
  436. lua_util.debugm(N, task, 'no key in map for %s', dkim_domain)
  437. end
  438. end
  439. if #p == 0 and not settings.try_fallback then
  440. lua_util.debugm(N, task, 'dkim unconfigured and fallback disabled')
  441. return false,{}
  442. end
  443. if not settings.use_redis then
  444. insert_or_update_prop(N, task, p, 'key',
  445. 'default path', settings.path)
  446. end
  447. insert_or_update_prop(N, task, p, 'selector',
  448. 'default selector', settings.selector)
  449. if settings.check_violation then
  450. if not check_violation(N, task, p.domain) then
  451. return false,{}
  452. end
  453. end
  454. insert_or_update_prop(N, task, p, 'domain', 'dkim_domain',
  455. dkim_domain)
  456. return true,p
  457. end
  458. exports.prepare_dkim_signing = prepare_dkim_signing
  459. exports.sign_using_redis = function(N, task, settings, selectors, sign_func, err_func)
  460. local lua_redis = require "lua_redis"
  461. local function try_redis_key(selector, p)
  462. p.key = nil
  463. p.selector = selector
  464. local rk = string.format('%s.%s', p.selector, p.domain)
  465. local function redis_key_cb(err, data)
  466. if err then
  467. err_func(string.format("cannot make request to load DKIM key for %s: %s",
  468. rk, err))
  469. elseif type(data) ~= 'string' then
  470. lua_util.debugm(N, task, "missing DKIM key for %s", rk)
  471. else
  472. p.rawkey = data
  473. lua_util.debugm(N, task, 'found and parsed key for %s:%s in Redis',
  474. p.domain, p.selector)
  475. sign_func(task, p)
  476. end
  477. end
  478. local rret = lua_redis.redis_make_request(task,
  479. settings.redis_params, -- connect params
  480. rk, -- hash key
  481. false, -- is write
  482. redis_key_cb, --callback
  483. 'HGET', -- command
  484. {settings.key_prefix, rk} -- arguments
  485. )
  486. if not rret then
  487. err_func(task,
  488. string.format( "cannot make request to load DKIM key for %s", rk))
  489. end
  490. end
  491. for _, p in ipairs(selectors) do
  492. if settings.selector_prefix then
  493. logger.infox(task, "using selector prefix '%s' for domain '%s'",
  494. settings.selector_prefix, p.domain);
  495. local function redis_selector_cb(err, data)
  496. if err or type(data) ~= 'string' then
  497. err_func(task, string.format("cannot make request to load DKIM selector for domain %s: %s",
  498. p.domain, err))
  499. else
  500. try_redis_key(data, p)
  501. end
  502. end
  503. local rret = lua_redis.redis_make_request(task,
  504. settings.redis_params, -- connect params
  505. p.domain, -- hash key
  506. false, -- is write
  507. redis_selector_cb, --callback
  508. 'HGET', -- command
  509. {settings.selector_prefix, p.domain} -- arguments
  510. )
  511. if not rret then
  512. err_func(task, string.format("cannot make Redis request to load DKIM selector for domain %s",
  513. p.domain))
  514. end
  515. else
  516. try_redis_key(p.selector, p)
  517. end
  518. end
  519. end
  520. exports.sign_using_vault = function(N, task, settings, selectors, sign_func, err_func)
  521. local http = require "rspamd_http"
  522. local ucl = require "ucl"
  523. local full_url = string.format('%s/v1/%s/%s',
  524. settings.vault_url, settings.vault_path or 'dkim', selectors.domain)
  525. local function vault_callback(err, code, body, _)
  526. if code ~= 200 then
  527. err_func(task, string.format('cannot request data from the vault url: %s; %s (%s)',
  528. full_url, err, body))
  529. else
  530. local parser = ucl.parser()
  531. local res,parser_err = parser:parse_string(body)
  532. if not res then
  533. err_func(task, string.format('vault reply for %s (data=%s) cannot be parsed: %s',
  534. full_url, body, parser_err))
  535. else
  536. local obj = parser:get_object()
  537. if not obj or not obj.data then
  538. err_func(task, string.format('vault reply for %s (data=%s) is invalid, no data',
  539. full_url, body))
  540. else
  541. local elts = obj.data.selectors or {}
  542. -- Filter selectors by time/sanity
  543. local function is_selector_valid(p)
  544. if not p.key or not p.selector then
  545. return false
  546. end
  547. if p.valid_start then
  548. -- Check start time
  549. if rspamd_util.get_time() < tonumber(p.valid_start) then
  550. return false
  551. end
  552. end
  553. if p.valid_end then
  554. if rspamd_util.get_time() >= tonumber(p.valid_end) then
  555. return false
  556. end
  557. end
  558. return true
  559. end
  560. fun.each(function(p)
  561. local dkim_sign_data = {
  562. rawkey = p.key,
  563. selector = p.selector,
  564. domain = p.domain or selectors.domain,
  565. alg = p.alg,
  566. }
  567. lua_util.debugm(N, task, 'found and parsed key for %s:%s in Vault',
  568. dkim_sign_data.domain, dkim_sign_data.selector)
  569. sign_func(task, dkim_sign_data)
  570. end, fun.filter(is_selector_valid, elts))
  571. end
  572. end
  573. end
  574. end
  575. local ret = http.request{
  576. task = task,
  577. url = full_url,
  578. callback = vault_callback,
  579. timeout = settings.http_timeout or 5.0,
  580. no_ssl_verify = settings.no_ssl_verify,
  581. keepalive = true,
  582. headers = {
  583. ['X-Vault-Token'] = settings.vault_token,
  584. },
  585. }
  586. if not ret then
  587. err_func(task, string.format("cannot make HTTP request to load DKIM data domain %s",
  588. selectors.domain))
  589. end
  590. end
  591. exports.validate_signing_settings = function(settings)
  592. return settings.use_redis or
  593. settings.path or
  594. settings.domain or
  595. settings.path_map or
  596. settings.selector_map or
  597. settings.use_http_headers or
  598. (settings.signing_table and settings.key_table) or
  599. (settings.use_vault and settings.vault_url and settings.vault_token) or
  600. settings.sign_condition
  601. end
  602. exports.process_signing_settings = function(N, settings, opts)
  603. local lua_maps = require "lua_maps"
  604. for k,v in pairs(opts) do
  605. if k == 'sign_networks' then
  606. settings[k] = lua_maps.map_add(N, k, 'radix', 'DKIM signing networks')
  607. elseif k == 'path_map' then
  608. settings[k] = lua_maps.map_add(N, k, 'map', 'Paths to DKIM signing keys')
  609. elseif k == 'selector_map' then
  610. settings[k] = lua_maps.map_add(N, k, 'map', 'DKIM selectors')
  611. elseif k == 'signing_table' then
  612. settings[k] = lua_maps.map_add(N, k, 'glob', 'DKIM signing table')
  613. elseif k == 'key_table' then
  614. settings[k] = lua_maps.map_add(N, k, 'glob', 'DKIM keys table')
  615. elseif k == 'vault_domains' then
  616. settings[k] = lua_maps.map_add(N, k, 'glob', 'DKIM signing domains in vault')
  617. elseif k == 'sign_condition' then
  618. local ret,f = lua_util.callback_from_string(v)
  619. if ret then
  620. settings[k] = f
  621. else
  622. logger.errx(rspamd_config, 'cannot load sign condition %s: %s', v, f)
  623. end
  624. else
  625. settings[k] = v
  626. end
  627. end
  628. end
  629. return exports