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.

dkim_signing.lua 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. --[[
  2. Copyright (c) 2016, Andrew Lewis <nerf@judo.za.org>
  3. Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
  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 lua_util = require "lua_util"
  15. local rspamd_logger = require "rspamd_logger"
  16. local dkim_sign_tools = require "lua_dkim_tools"
  17. local lua_redis = require "lua_redis"
  18. local lua_mime = require "lua_mime"
  19. if confighelp then
  20. return
  21. end
  22. local settings = {
  23. allow_envfrom_empty = true,
  24. allow_hdrfrom_mismatch = false,
  25. allow_hdrfrom_mismatch_local = false,
  26. allow_hdrfrom_mismatch_sign_networks = false,
  27. allow_hdrfrom_multiple = false,
  28. allow_username_mismatch = false,
  29. allow_pubkey_mismatch = true,
  30. sign_authenticated = true,
  31. allowed_ids = nil,
  32. forbidden_ids = nil,
  33. check_pubkey = false,
  34. domain = {},
  35. path = string.format('%s/%s/%s', rspamd_paths['DBDIR'], 'dkim', '$domain.$selector.key'),
  36. sign_local = true,
  37. selector = 'dkim',
  38. symbol = 'DKIM_SIGNED',
  39. try_fallback = true,
  40. use_domain = 'header',
  41. use_esld = true,
  42. use_redis = false,
  43. key_prefix = 'dkim_keys', -- default hash name
  44. use_milter_headers = false, -- use milter headers instead of `dkim_signature`
  45. }
  46. local N = 'dkim_signing'
  47. local redis_params
  48. local sign_func = rspamd_plugins.dkim.sign
  49. local function insert_sign_results(task, ret, hdr, dkim_params)
  50. if settings.use_milter_headers then
  51. lua_mime.modify_headers(task, {
  52. add = {
  53. ['DKIM-Signature'] = { order = 1, value = hdr },
  54. }
  55. })
  56. end
  57. if ret then
  58. task:insert_result(settings.symbol, 1.0, string.format('%s:s=%s',
  59. dkim_params.domain, dkim_params.selector))
  60. end
  61. end
  62. local function do_sign(task, p)
  63. if settings.use_milter_headers then
  64. p.no_cache = true -- Disable caching in rspamd_mempool
  65. end
  66. if settings.check_pubkey then
  67. local resolve_name = p.selector .. "._domainkey." .. p.domain
  68. task:get_resolver():resolve_txt({
  69. task = task,
  70. name = resolve_name,
  71. callback = function(_, _, results, err)
  72. if not err and results and results[1] then
  73. p.pubkey = results[1]
  74. p.strict_pubkey_check = not settings.allow_pubkey_mismatch
  75. elseif not settings.allow_pubkey_mismatch then
  76. rspamd_logger.infox(task, 'public key for domain %s/%s is not found: %s, skip signing',
  77. p.domain, p.selector, err)
  78. return
  79. else
  80. rspamd_logger.infox(task, 'public key for domain %s/%s is not found: %s',
  81. p.domain, p.selector, err)
  82. end
  83. local sret, hdr = sign_func(task, p)
  84. insert_sign_results(task, sret, hdr, p)
  85. end,
  86. forced = true
  87. })
  88. else
  89. local sret, hdr = sign_func(task, p)
  90. insert_sign_results(task, sret, hdr, p)
  91. end
  92. end
  93. local function sign_error(task, msg)
  94. rspamd_logger.errx(task, 'signing failure: %s', msg)
  95. end
  96. local function dkim_signing_cb(task)
  97. local ret, selectors = dkim_sign_tools.prepare_dkim_signing(N, task, settings)
  98. if not ret or #selectors == 0 then
  99. return
  100. end
  101. if settings.use_redis then
  102. dkim_sign_tools.sign_using_redis(N, task, settings, selectors, do_sign, sign_error)
  103. else
  104. if selectors.vault then
  105. dkim_sign_tools.sign_using_vault(N, task, settings, selectors, do_sign, sign_error)
  106. else
  107. if #selectors > 0 then
  108. for _, k in ipairs(selectors) do
  109. -- templates
  110. if k.key then
  111. k.key = lua_util.template(k.key, {
  112. domain = k.domain,
  113. selector = k.selector
  114. })
  115. lua_util.debugm(N, task, 'using key "%s", use selector "%s" for domain "%s"',
  116. k.key, k.selector, k.domain)
  117. end
  118. do_sign(task, k)
  119. end
  120. else
  121. rspamd_logger.infox(task, 'key path or dkim selector unconfigured; no signing')
  122. return false
  123. end
  124. end
  125. end
  126. end
  127. local opts = rspamd_config:get_all_opt('dkim_signing')
  128. if not opts then
  129. return
  130. end
  131. dkim_sign_tools.process_signing_settings(N, settings, opts)
  132. if not dkim_sign_tools.validate_signing_settings(settings) then
  133. rspamd_logger.infox(rspamd_config, 'mandatory parameters missing, disable dkim signing')
  134. lua_util.disable_module(N, "config")
  135. return
  136. end
  137. if settings.use_redis then
  138. redis_params = lua_redis.parse_redis_server('dkim_signing')
  139. if not redis_params then
  140. rspamd_logger.errx(rspamd_config,
  141. 'no servers are specified, but module is configured to load keys from redis, disable dkim signing')
  142. lua_util.disable_module(N, "redis")
  143. return
  144. end
  145. settings.redis_params = redis_params
  146. end
  147. local sym_reg_tbl = {
  148. name = settings['symbol'],
  149. callback = dkim_signing_cb,
  150. groups = { "policies", "dkim" },
  151. flags = 'ignore_passthrough',
  152. score = 0.0,
  153. }
  154. if type(settings.allowed_ids) == 'table' then
  155. sym_reg_tbl.allowed_ids = settings.allowed_ids
  156. end
  157. if type(settings.forbidden_ids) == 'table' then
  158. sym_reg_tbl.forbidden_ids = settings.forbidden_ids
  159. end
  160. rspamd_config:register_symbol(sym_reg_tbl)
  161. -- Add dependency on DKIM checks
  162. rspamd_config:register_dependency(settings['symbol'], 'DKIM_CHECK')