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.

mid.lua 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. --[[
  2. Copyright (c) 2022, Vsevolod Stakhov <vsevolod@rspamd.com>
  3. Copyright (c) 2016, Steve Freegard <steve@freegard.name>
  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 rspamd_util = require "rspamd_util"
  15. local function mid_check_func(task)
  16. local mid = task:get_header('Message-ID')
  17. if not mid then
  18. return false
  19. end
  20. -- Check for 'bare' IP addresses in RHS
  21. if mid:find("@%d+%.%d+%.%d+%.%d+>$") then
  22. task:insert_result('MID_BARE_IP', 1.0)
  23. end
  24. -- Check for non-FQDN RHS
  25. if mid:find("@[^%.]+>?$") then
  26. task:insert_result('MID_RHS_NOT_FQDN', 1.0)
  27. end
  28. -- Check for missing <>'s
  29. if not mid:find('^<[^>]+>$') then
  30. task:insert_result('MID_MISSING_BRACKETS', 1.0)
  31. end
  32. -- Check for IP literal in RHS
  33. if mid:find("@%[%d+%.%d+%.%d+%.%d+%]") then
  34. task:insert_result('MID_RHS_IP_LITERAL', 1.0)
  35. end
  36. -- Check From address attributes against MID
  37. local from = task:get_from(2)
  38. local fd
  39. if (from and from[1] and from[1].domain and from[1].domain ~= '') then
  40. fd = from[1].domain:lower()
  41. local _, _, md = mid:find("@([^>]+)>?$")
  42. -- See if all or part of the From address
  43. -- can be found in the Message-ID
  44. -- extract tld
  45. local fdtld = nil
  46. local mdtld = nil
  47. if md then
  48. fdtld = rspamd_util.get_tld(fd)
  49. mdtld = rspamd_util.get_tld(md)
  50. end
  51. if (mid:lower():find(from[1].addr:lower(), 1, true)) then
  52. task:insert_result('MID_CONTAINS_FROM', 1.0)
  53. elseif (md and fd == md:lower()) then
  54. task:insert_result('MID_RHS_MATCH_FROM', 1.0)
  55. elseif (mdtld ~= nil and fdtld ~= nil and mdtld:lower() == fdtld) then
  56. task:insert_result('MID_RHS_MATCH_FROMTLD', 1.0)
  57. end
  58. end
  59. -- Check To address attributes against MID
  60. local to = task:get_recipients(2)
  61. if (to and to[1] and to[1].domain and to[1].domain ~= '') then
  62. local td = to[1].domain:lower()
  63. local _, _, md = mid:find("@([^>]+)>?$")
  64. -- Skip if from domain == to domain
  65. if ((fd and fd ~= td) or not fd) then
  66. -- See if all or part of the To address
  67. -- can be found in the Message-ID
  68. if (mid:lower():find(to[1].addr:lower(), 1, true)) then
  69. task:insert_result('MID_CONTAINS_TO', 1.0)
  70. elseif (md and td == md:lower()) then
  71. task:insert_result('MID_RHS_MATCH_TO', 1.0)
  72. end
  73. end
  74. end
  75. end
  76. -- MID checks from Steve Freegard
  77. local check_mid_id = rspamd_config:register_symbol({
  78. name = 'CHECK_MID',
  79. score = 0.0,
  80. group = 'mid',
  81. type = 'callback,mime',
  82. callback = mid_check_func
  83. })
  84. rspamd_config:register_virtual_symbol('MID_BARE_IP', 1.0, check_mid_id)
  85. rspamd_config:set_metric_symbol('MID_BARE_IP', 2.0, 'Message-ID RHS is a bare IP address', 'default', 'Message ID')
  86. rspamd_config:register_virtual_symbol('MID_RHS_NOT_FQDN', 1.0, check_mid_id)
  87. rspamd_config:set_metric_symbol('MID_RHS_NOT_FQDN', 0.5,
  88. 'Message-ID RHS is not a fully-qualified domain name', 'default', 'Message ID')
  89. rspamd_config:register_virtual_symbol('MID_MISSING_BRACKETS', 1.0, check_mid_id)
  90. rspamd_config:set_metric_symbol('MID_MISSING_BRACKETS', 0.5, 'Message-ID is missing <>\'s', 'default', 'Message ID')
  91. rspamd_config:register_virtual_symbol('MID_RHS_IP_LITERAL', 1.0, check_mid_id)
  92. rspamd_config:set_metric_symbol('MID_RHS_IP_LITERAL', 0.5, 'Message-ID RHS is an IP-literal', 'default', 'Message ID')
  93. rspamd_config:register_virtual_symbol('MID_CONTAINS_FROM', 1.0, check_mid_id)
  94. rspamd_config:set_metric_symbol('MID_CONTAINS_FROM', 1.0, 'Message-ID contains From address', 'default', 'Message ID')
  95. rspamd_config:register_virtual_symbol('MID_RHS_MATCH_FROM', 1.0, check_mid_id)
  96. rspamd_config:set_metric_symbol('MID_RHS_MATCH_FROM', 0.0,
  97. 'Message-ID RHS matches From domain', 'default', 'Message ID')
  98. rspamd_config:register_virtual_symbol('MID_RHS_MATCH_FROMTLD', 1.0, check_mid_id)
  99. rspamd_config:set_metric_symbol('MID_RHS_MATCH_FROMTLD', 0.0,
  100. 'Message-ID RHS matches From domain tld', 'default', 'Message ID')
  101. rspamd_config:register_virtual_symbol('MID_CONTAINS_TO', 1.0, check_mid_id)
  102. rspamd_config:set_metric_symbol('MID_CONTAINS_TO', 1.0, 'Message-ID contains To address', 'default', 'Message ID')
  103. rspamd_config:register_virtual_symbol('MID_RHS_MATCH_TO', 1.0, check_mid_id)
  104. rspamd_config:set_metric_symbol('MID_RHS_MATCH_TO', 1.0, 'Message-ID RHS matches To domain', 'default', 'Message ID')
  105. -- Another check from https://github.com/rspamd/rspamd/issues/4299
  106. rspamd_config:register_symbol {
  107. type = 'normal,mime',
  108. group = 'mid',
  109. name = 'MID_END_EQ_FROM_USER_PART',
  110. description = 'Message-ID RHS (after @) and MIME from local part are the same',
  111. score = 4.0,
  112. callback = function(task)
  113. local mid = task:get_header('Message-ID')
  114. if not mid then
  115. return
  116. end
  117. local mime_from = task:get_from('mime')
  118. local _, _, mid_realm = mid:find("@([a-z]+)>?$")
  119. if mid_realm and mime_from and mime_from[1] and mime_from[1].user then
  120. if (mid_realm == mime_from[1].user) then
  121. return true
  122. end
  123. end
  124. end
  125. }