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_auth_results.lua 7.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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 global = require "global_functions"
  15. local rspamd_util = require "rspamd_util"
  16. local default_settings = {
  17. spf_symbols = {
  18. pass = 'R_SPF_ALLOW',
  19. fail = 'R_SPF_FAIL',
  20. softfail = 'R_SPF_SOFTFAIL',
  21. neutral = 'R_SPF_NEUTRAL',
  22. temperror = 'R_SPF_DNSFAIL',
  23. none = 'R_SPF_NA',
  24. permerror = 'R_SPF_PERMFAIL',
  25. },
  26. dmarc_symbols = {
  27. pass = 'DMARC_POLICY_ALLOW',
  28. permerror = 'DMARC_BAD_POLICY',
  29. temperror = 'DMARC_DNSFAIL',
  30. none = 'DMARC_NA',
  31. reject = 'DMARC_POLICY_REJECT',
  32. softfail = 'DMARC_POLICY_SOFTFAIL',
  33. quarantine = 'DMARC_POLICY_QUARANTINE',
  34. },
  35. arc_symbols = {
  36. pass = 'ARC_ALLOW',
  37. permerror = 'ARC_INVALID',
  38. temperror = 'ARC_DNSFAIL',
  39. none = 'ARC_NA',
  40. reject = 'ARC_REJECT',
  41. },
  42. add_smtp_user = true,
  43. }
  44. local exports = {}
  45. local local_hostname = rspamd_util.get_hostname()
  46. local function gen_auth_results(task, settings)
  47. local auth_results, hdr_parts = {}, {}
  48. if not settings then
  49. settings = default_settings
  50. end
  51. local auth_types = {
  52. dkim = settings.dkim_symbols,
  53. dmarc = settings.dmarc_symbols,
  54. spf = settings.spf_symbols,
  55. arc = settings.arc_symbols,
  56. }
  57. local common = {
  58. symbols = {}
  59. }
  60. local mta_hostname = task:get_request_header('MTA-Name') or
  61. task:get_request_header('MTA-Tag')
  62. if mta_hostname then
  63. mta_hostname = tostring(mta_hostname)
  64. else
  65. mta_hostname = local_hostname
  66. end
  67. table.insert(hdr_parts, mta_hostname)
  68. for auth_type, symbols in pairs(auth_types) do
  69. for key, sym in pairs(symbols) do
  70. if not common.symbols.sym then
  71. local s = task:get_symbol(sym)
  72. if not s then
  73. common.symbols[sym] = false
  74. else
  75. common.symbols[sym] = s
  76. if not auth_results[auth_type] then
  77. auth_results[auth_type] = {key}
  78. else
  79. table.insert(auth_results[auth_type], key)
  80. end
  81. if auth_type ~= 'dkim' then
  82. break
  83. end
  84. end
  85. end
  86. end
  87. end
  88. local dkim_results = task:get_dkim_results()
  89. -- For each signature we set authentication results
  90. -- dkim=neutral (body hash did not verify) header.d=example.com header.s=sel header.b=fA8VVvJ8;
  91. -- dkim=neutral (body hash did not verify) header.d=example.com header.s=sel header.b=f8pM8o90;
  92. for _,dres in ipairs(dkim_results) do
  93. local ar_string = 'none'
  94. if dres.result == 'reject' then
  95. ar_string = 'fail' -- imply failure, not neutral
  96. elseif dres.result == 'allow' then
  97. ar_string = 'pass'
  98. elseif dres.result == 'bad record' or dres.result == 'permerror' then
  99. ar_string = 'permerror'
  100. elseif dres.result == 'tempfail' then
  101. ar_string = 'temperror'
  102. end
  103. local hdr = {}
  104. hdr[1] = string.format('dkim=%s', ar_string)
  105. if dres.fail_reason then
  106. hdr[#hdr + 1] = string.format('(%s)', dres.fail_reason)
  107. end
  108. if dres.domain then
  109. hdr[#hdr + 1] = string.format('header.d=%s', dres.domain)
  110. end
  111. if dres.selector then
  112. hdr[#hdr + 1] = string.format('header.s=%s', dres.selector)
  113. end
  114. if dres.bhash then
  115. hdr[#hdr + 1] = string.format('header.b=%s', dres.bhash)
  116. end
  117. table.insert(hdr_parts, table.concat(hdr, ' '))
  118. end
  119. for auth_type, keys in pairs(auth_results) do
  120. for _, key in ipairs(keys) do
  121. local hdr = ''
  122. if auth_type == 'dmarc' and key ~= 'none' then
  123. local opts = common.symbols[auth_types['dmarc'][key]][1]['options'] or {}
  124. hdr = hdr .. 'dmarc='
  125. if key == 'reject' or key == 'quarantine' or key == 'softfail' then
  126. hdr = hdr .. 'fail'
  127. else
  128. hdr = hdr .. key
  129. end
  130. if key == 'pass' then
  131. hdr = hdr .. ' (policy=' .. opts[2] .. ')'
  132. hdr = hdr .. ' header.from=' .. opts[1]
  133. elseif key ~= 'none' then
  134. local t = global.rspamd_str_split(opts[1], ' : ')
  135. local dom = t[1]
  136. local rsn = t[2]
  137. if rsn then
  138. hdr = hdr .. ' reason="' .. rsn .. '"'
  139. end
  140. hdr = hdr .. ' header.from=' .. dom
  141. if key == 'softfail' then
  142. hdr = hdr .. ' (policy=none)'
  143. else
  144. hdr = hdr .. ' (policy=' .. key .. ')'
  145. end
  146. end
  147. table.insert(hdr_parts, hdr)
  148. elseif auth_type == 'arc' and key ~= 'none' then
  149. if common.symbols[auth_types['arc'][key]][1] then
  150. local opts = common.symbols[auth_types['arc'][key]][1]['options'] or {}
  151. for _, v in ipairs(opts) do
  152. hdr = hdr .. auth_type .. '=' .. key .. ' (' .. v .. ')'
  153. table.insert(hdr_parts, hdr)
  154. end
  155. end
  156. elseif auth_type == 'spf' and key ~= 'none' then
  157. -- Main type
  158. local sender
  159. local sender_type
  160. local smtp_from = task:get_from('smtp')
  161. if smtp_from and
  162. smtp_from[1] and
  163. smtp_from[1]['addr'] ~= '' and
  164. smtp_from[1]['addr'] ~= nil then
  165. sender = smtp_from[1]['addr']
  166. sender_type = 'smtp.mailfrom'
  167. else
  168. local helo = task:get_helo()
  169. if helo then
  170. sender = helo
  171. sender_type = 'smtp.helo'
  172. end
  173. end
  174. if sender and sender_type then
  175. -- Comment line
  176. local comment = ''
  177. if key == 'pass' then
  178. comment = string.format('%s: domain of %s designates %s as permitted sender',
  179. mta_hostname, sender, tostring(task:get_from_ip() or 'unknown'))
  180. elseif key == 'fail' then
  181. comment = string.format('%s: domain of %s does not designate %s as permitted sender',
  182. mta_hostname, sender, tostring(task:get_from_ip() or 'unknown'))
  183. elseif key == 'neutral' or key == 'softfail' then
  184. comment = string.format('%s: %s is neither permitted nor denied by domain of %s',
  185. mta_hostname, tostring(task:get_from_ip() or 'unknown'), sender)
  186. elseif key == 'permerror' then
  187. comment = string.format('%s: domain of %s uses mechanism not recognized by this client',
  188. mta_hostname, sender)
  189. elseif key == 'temperror' then
  190. comment = string.format('%s: error in processing during lookup of %s: DNS error',
  191. mta_hostname, sender)
  192. end
  193. hdr = string.format('%s=%s (%s) %s=%s', auth_type, key,
  194. comment, sender_type, sender)
  195. else
  196. hdr = string.format('%s=%s', auth_type, key)
  197. end
  198. table.insert(hdr_parts, hdr)
  199. end
  200. end
  201. end
  202. local u = task:get_user()
  203. local smtp_from = task:get_from('smtp')
  204. if u and smtp_from then
  205. local hdr = {[1] = 'auth=pass'}
  206. if settings['add_smtp_user'] then
  207. table.insert(hdr,'smtp.auth=' .. u)
  208. end
  209. if smtp_from[1]['addr'] then
  210. table.insert(hdr,'smtp.mailfrom=' .. smtp_from[1]['addr'])
  211. end
  212. table.insert(hdr_parts, table.concat(hdr,' '))
  213. end
  214. if #hdr_parts > 0 then
  215. return table.concat(hdr_parts, '; ')
  216. end
  217. return nil
  218. end
  219. exports.gen_auth_results = gen_auth_results
  220. return exports