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 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  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 function check_violation(N, task, domain, selector)
  20. -- Check for DKIM_REJECT
  21. local sym_check = 'R_DKIM_REJECT'
  22. if N == 'arc' then sym_check = 'ARC_REJECT' end
  23. if task:has_symbol(sym_check) then
  24. local sym = task:get_symbol(sym_check)
  25. logger.infox(task, 'skip signing for %s:%s: violation %s found: %s',
  26. domain, selector, sym_check, sym.options)
  27. return false
  28. end
  29. return true
  30. end
  31. local function parse_dkim_http_headers(N, task, settings)
  32. -- Configure headers
  33. local headers = {
  34. sign_header = settings.http_sign_header or "PerformDkimSign",
  35. sign_on_reject_header = settings.http_sign_on_reject_header_header or 'SignOnAuthFailed',
  36. domain_header = settings.http_domain_header or 'DkimDomain',
  37. selector_header = settings.http_selector_header or 'DkimSelector',
  38. key_header = settings.http_key_header or 'DkimPrivateKey'
  39. }
  40. if task:get_request_header(headers.sign_header) then
  41. local domain = task:get_request_header(headers.domain_header)
  42. local selector = task:get_request_header(headers.selector_header)
  43. local key = task:get_request_header(headers.key_header)
  44. if not (domain and selector and key) then
  45. logger.errx(task, 'missing required headers to sign email')
  46. return false,{}
  47. end
  48. -- Now check if we need to check the existing auth
  49. local hdr = task:get_request_header(headers.sign_on_reject_header)
  50. if not hdr or tostring(hdr) == '0' or tostring(hdr) == 'false' then
  51. if not check_violation(N, task, domain, selector) then
  52. return false, {}
  53. end
  54. end
  55. return true,{
  56. rawkey = tostring(key),
  57. domain = tostring(domain),
  58. selector = tostring(selector)
  59. }
  60. end
  61. lua_util.debugm(N, task, 'no sign header %s', headers.sign_header)
  62. return false,{}
  63. end
  64. local function prepare_dkim_signing(N, task, settings)
  65. local is_local, is_sign_networks
  66. if settings.use_http_headers then
  67. return parse_dkim_http_headers(N, task, settings)
  68. end
  69. local auser = task:get_user()
  70. local ip = task:get_from_ip()
  71. if ip and ip:is_local() then
  72. is_local = true
  73. end
  74. if settings.auth_only and auser then
  75. lua_util.debugm(N, task, 'user is authenticated')
  76. elseif (settings.sign_networks and settings.sign_networks:get_key(ip)) then
  77. is_sign_networks = true
  78. lua_util.debugm(N, task, 'mail is from address in sign_networks')
  79. elseif settings.sign_local and is_local then
  80. lua_util.debugm(N, task, 'mail is from local address')
  81. elseif settings.sign_inbound and not is_local and not auser then
  82. lua_util.debugm(N, task, 'mail was sent to us')
  83. else
  84. lua_util.debugm(N, task, 'ignoring unauthenticated mail')
  85. return false,{}
  86. end
  87. local efrom = task:get_from('smtp')
  88. if not settings.allow_envfrom_empty and
  89. #(((efrom or E)[1] or E).addr or '') == 0 then
  90. lua_util.debugm(N, task, 'empty envelope from not allowed')
  91. return false,{}
  92. end
  93. local hfrom = task:get_from('mime')
  94. if not settings.allow_hdrfrom_multiple and (hfrom or E)[2] then
  95. lua_util.debugm(N, task, 'multiple header from not allowed')
  96. return false,{}
  97. end
  98. local eto = task:get_recipients(0)
  99. local dkim_domain
  100. local hdom = ((hfrom or E)[1] or E).domain
  101. local edom = ((efrom or E)[1] or E).domain
  102. local tdom = ((eto or E)[1] or E).domain
  103. local udom = string.match(auser or '', '.*@(.*)')
  104. local function get_dkim_domain(dtype)
  105. if settings[dtype] == 'header' then
  106. return hdom
  107. elseif settings[dtype] == 'envelope' then
  108. return edom
  109. elseif settings[dtype] == 'auth' then
  110. return udom
  111. elseif settings[dtype] == 'recipient' then
  112. return tdom
  113. end
  114. end
  115. if hdom then
  116. hdom = hdom:lower()
  117. end
  118. if edom then
  119. edom = edom:lower()
  120. end
  121. if udom then
  122. udom = udom:lower()
  123. end
  124. if tdom then
  125. tdom = tdom:lower()
  126. end
  127. if settings.use_domain_sign_networks and is_sign_networks then
  128. dkim_domain = get_dkim_domain('use_domain_sign_networks')
  129. lua_util.debugm(N, task, 'sign_networks: use domain(%s) for signature: %s',
  130. settings.use_domain_sign_networks, dkim_domain)
  131. elseif settings.use_domain_sign_local and is_local then
  132. dkim_domain = get_dkim_domain('use_domain_sign_local')
  133. lua_util.debugm(N, task, 'local: use domain(%s) for signature: %s',
  134. settings.use_domain_sign_local, dkim_domain)
  135. elseif settings.use_domain_sign_inbound and not is_local and not auser then
  136. dkim_domain = get_dkim_domain('use_domain_sign_inbound')
  137. lua_util.debugm(N, task, 'inbound: use domain(%s) for signature: %s',
  138. settings.use_domain_sign_inbound, dkim_domain)
  139. else
  140. dkim_domain = get_dkim_domain('use_domain')
  141. lua_util.debugm(N, task, 'use domain(%s) for signature: %s',
  142. settings.use_domain, dkim_domain)
  143. end
  144. if not dkim_domain then
  145. lua_util.debugm(N, task, 'could not extract dkim domain')
  146. return false,{}
  147. end
  148. if settings.use_esld then
  149. dkim_domain = rspamd_util.get_tld(dkim_domain)
  150. if hdom then
  151. hdom = rspamd_util.get_tld(hdom)
  152. end
  153. if edom then
  154. edom = rspamd_util.get_tld(edom)
  155. end
  156. end
  157. lua_util.debugm(N, task, 'final DKIM domain: %s', dkim_domain)
  158. if edom and hdom and not settings.allow_hdrfrom_mismatch and hdom ~= edom then
  159. if settings.allow_hdrfrom_mismatch_local and is_local then
  160. lua_util.debugm(N, task, 'domain mismatch allowed for local IP: %1 != %2', hdom, edom)
  161. elseif settings.allow_hdrfrom_mismatch_sign_networks and is_sign_networks then
  162. lua_util.debugm(N, task, 'domain mismatch allowed for sign_networks: %1 != %2', hdom, edom)
  163. else
  164. lua_util.debugm(N, task, 'domain mismatch not allowed: %1 != %2', hdom, edom)
  165. return false,{}
  166. end
  167. end
  168. if auser and not settings.allow_username_mismatch then
  169. if not udom then
  170. lua_util.debugm(N, task, 'couldnt find domain in username')
  171. return false,{}
  172. end
  173. if settings.use_esld then
  174. udom = rspamd_util.get_tld(udom)
  175. end
  176. if udom ~= dkim_domain then
  177. lua_util.debugm(N, task, 'user domain mismatch')
  178. return false,{}
  179. end
  180. end
  181. local p = {}
  182. if settings.domain[dkim_domain] then
  183. p.selector = settings.domain[dkim_domain].selector
  184. p.key = settings.domain[dkim_domain].path
  185. end
  186. if not p.key and p.selector then
  187. local key_var = "dkim_key"
  188. local selector_var = "dkim_selector"
  189. if N == "arc" then
  190. key_var = "arc_key"
  191. selector_var = "arc_selector"
  192. end
  193. p.key = task:get_mempool():get_variable(key_var)
  194. local selector_override = task:get_mempool():get_variable(selector_var)
  195. if selector_override then
  196. p.selector = selector_override
  197. end
  198. if (not p.key or not p.selector) and (not (settings.try_fallback or
  199. settings.use_redis or settings.selector_map
  200. or settings.path_map)) then
  201. lua_util.debugm(N, task, 'dkim unconfigured and fallback disabled')
  202. return false,{}
  203. end
  204. lua_util.debugm(N, task, 'override selector and key to %s:%s', p.key, p.selector)
  205. end
  206. if not p.selector and settings.selector_map then
  207. local data = settings.selector_map:get_key(dkim_domain)
  208. if data then
  209. p.selector = data
  210. lua_util.debugm(N, task, 'override selector to "%s" using selector_map', p.selector)
  211. elseif not settings.try_fallback then
  212. lua_util.debugm(N, task, 'no selector for %s', dkim_domain)
  213. return false,{}
  214. end
  215. end
  216. if not p.key and settings.path_map then
  217. local data = settings.path_map:get_key(dkim_domain)
  218. if data then
  219. p.key = data
  220. lua_util.debugm(N, task, 'override key to "%s" using path_map', p.key)
  221. elseif not settings.try_fallback then
  222. lua_util.debugm(N, task, 'no key for %s', dkim_domain)
  223. return false,{}
  224. end
  225. end
  226. if not p.key then
  227. if not settings.use_redis then
  228. p.key = settings.path
  229. lua_util.debugm(N, task, 'use default key "%s" from path', p.key)
  230. end
  231. end
  232. if not p.selector then
  233. p.selector = settings.selector
  234. lua_util.debugm(N, task, 'use default selector "%s"', p.selector)
  235. end
  236. if settings.check_violation then
  237. if not check_violation(N, task, p.domain, p.selector) then
  238. return false,{}
  239. end
  240. end
  241. p.domain = dkim_domain
  242. return true,p
  243. end
  244. exports.prepare_dkim_signing = prepare_dkim_signing
  245. return exports