Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291
  1. --[[
  2. Copyright (c) 2011-2020, Vsevolod Stakhov <vsevolod@highsecure.ru>
  3. Copyright (c) 2013-2015, Andrew Lewis <nerf@judo.za.org>
  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. if confighelp then
  15. return
  16. end
  17. local hash = require 'rspamd_cryptobox_hash'
  18. local rspamd_logger = require 'rspamd_logger'
  19. local rspamd_util = require 'rspamd_util'
  20. local rspamd_ip = require "rspamd_ip"
  21. local fun = require 'fun'
  22. local lua_util = require 'lua_util'
  23. local selectors = require "lua_selectors"
  24. local bit = require 'bit'
  25. local lua_maps = require "lua_maps"
  26. local rbl_common = require "plugins/rbl"
  27. local rspamd_url = require "rspamd_url"
  28. -- This plugin implements various types of RBL checks
  29. -- Documentation can be found here:
  30. -- https://rspamd.com/doc/modules/rbl.html
  31. local E = {}
  32. local N = 'rbl'
  33. -- Checks that could be performed by rbl module
  34. local local_exclusions
  35. local white_symbols = {}
  36. local black_symbols = {}
  37. local monitored_addresses = {}
  38. local known_selectors = {} -- map from selector string to selector id
  39. local url_flag_bits = rspamd_url.flags
  40. local function get_monitored(rbl)
  41. local function is_random_monitored()
  42. -- Explicit definition
  43. if type(rbl.random_monitored) == 'boolean' then
  44. return rbl.random_monitored
  45. end
  46. -- We check 127.0.0.1 for merely RBLs with `from` or `received` and only if
  47. -- they don't have `no_ip` attribute at the same time
  48. --
  49. -- Convert to a boolean variable using the common idiom
  50. return (not (rbl.from or rbl.received)
  51. or rbl.no_ip)
  52. and true or false
  53. end
  54. local default_monitored = '1.0.0.127'
  55. local ret = {
  56. rcode = 'nxdomain',
  57. prefix = default_monitored,
  58. random = is_random_monitored(),
  59. }
  60. if rbl.monitored_address then
  61. ret.prefix = rbl.monitored_address
  62. end
  63. lua_util.debugm(N, rspamd_config,
  64. 'added monitored address: %s (%s random)',
  65. ret.prefix, ret.random)
  66. return ret
  67. end
  68. local function validate_dns(lstr)
  69. if lstr:match('%.%.') then
  70. -- two dots in a row
  71. return false
  72. end
  73. if not rspamd_util.is_valid_utf8(lstr) then
  74. -- invalid utf8 detected
  75. return false
  76. end
  77. for v in lstr:gmatch('[^%.]+') do
  78. if v:len() > 63 or v:match('^-') or v:match('-$') then
  79. -- too long label or weird labels
  80. return false
  81. end
  82. end
  83. return true
  84. end
  85. local function maybe_make_hash(data, rule)
  86. if rule.hash then
  87. local h = hash.create_specific(rule.hash, data)
  88. local s
  89. if rule.hash_format then
  90. if rule.hash_format == 'base32' then
  91. s = h:base32()
  92. elseif rule.hash_format == 'base64' then
  93. s = h:base64()
  94. else
  95. s = h:hex()
  96. end
  97. else
  98. s = h:hex()
  99. end
  100. if rule.hash_len then
  101. s = s:sub(1, rule.hash_len)
  102. end
  103. return s
  104. else
  105. return data
  106. end
  107. end
  108. local function is_excluded_ip(rip)
  109. if local_exclusions and local_exclusions:get_key(rip) then
  110. return true
  111. end
  112. return false
  113. end
  114. local function ip_to_rbl(ip)
  115. return table.concat(ip:inversed_str_octets(), '.')
  116. end
  117. local function gen_check_rcvd_conditions(rbl, received_total)
  118. local min_pos = tonumber(rbl['received_min_pos'])
  119. local max_pos = tonumber(rbl['received_max_pos'])
  120. local match_flags = rbl['received_flags']
  121. local nmatch_flags = rbl['received_nflags']
  122. local function basic_received_check(rh)
  123. if not (rh['real_ip'] and rh['real_ip']:is_valid()) then return false end
  124. if ((rh['real_ip']:get_version() == 6 and rbl['ipv6']) or
  125. (rh['real_ip']:get_version() == 4 and rbl['ipv4'])) and
  126. ((rbl['exclude_private_ips'] and not rh['real_ip']:is_local()) or
  127. not rbl['exclude_private_ips']) and ((rbl['exclude_local_ips'] and
  128. not is_excluded_ip(rh['real_ip'])) or not rbl['exclude_local_ips']) then
  129. return true
  130. else
  131. return false
  132. end
  133. end
  134. if not (max_pos or min_pos or match_flags or nmatch_flags) then
  135. return basic_received_check
  136. end
  137. return function(rh, pos)
  138. if not basic_received_check() then return false end
  139. local got_flags = rh['flags'] or E
  140. if min_pos then
  141. if min_pos < 0 then
  142. if min_pos == -1 then
  143. if (pos ~= received_total) then
  144. return false
  145. end
  146. else
  147. if pos <= (received_total - (min_pos*-1)) then
  148. return false
  149. end
  150. end
  151. elseif pos < min_pos then
  152. return false
  153. end
  154. end
  155. if max_pos then
  156. if max_pos < -1 then
  157. if (received_total - (max_pos*-1)) >= pos then
  158. return false
  159. end
  160. elseif max_pos > 0 then
  161. if pos > max_pos then
  162. return false
  163. end
  164. end
  165. end
  166. if match_flags then
  167. for _, flag in ipairs(match_flags) do
  168. if not got_flags[flag] then
  169. return false
  170. end
  171. end
  172. end
  173. if nmatch_flags then
  174. for _, flag in ipairs(nmatch_flags) do
  175. if got_flags[flag] then
  176. return false
  177. end
  178. end
  179. end
  180. return true
  181. end
  182. end
  183. local function rbl_dns_process(task, rbl, to_resolve, results, err, resolve_table_elt)
  184. local function make_option(ip, label)
  185. if ip then
  186. return string.format('%s:%s:%s',
  187. resolve_table_elt.orig,
  188. label,
  189. ip)
  190. else
  191. return string.format('%s:%s',
  192. resolve_table_elt.orig,
  193. label)
  194. end
  195. end
  196. local function insert_result(s, ip, label)
  197. if rbl.symbols_prefixes then
  198. local prefix = rbl.symbols_prefixes[label]
  199. if not prefix then
  200. rspamd_logger.warnx(task, 'unlisted symbol prefix for %s', label)
  201. task:insert_result(s, 1.0, make_option(ip, label))
  202. else
  203. task:insert_result(prefix .. '_' .. s, 1.0, make_option(ip, label))
  204. end
  205. else
  206. task:insert_result(s, 1.0, make_option(ip, label))
  207. end
  208. end
  209. local function insert_results(s, ip)
  210. for label in pairs(resolve_table_elt.what) do
  211. insert_result(s, ip, label)
  212. end
  213. end
  214. if err and (err ~= 'requested record is not found' and
  215. err ~= 'no records with this name') then
  216. rspamd_logger.infox(task, 'error looking up %s: %s', to_resolve, err)
  217. task:insert_result(rbl.symbol .. '_FAIL', 1, string.format('%s:%s',
  218. resolve_table_elt.orig, err))
  219. return
  220. end
  221. if not results then
  222. lua_util.debugm(N, task,
  223. 'DNS RESPONSE: label=%1 results=%2 error=%3 rbl=%4',
  224. to_resolve, false, err, rbl.symbol)
  225. return
  226. else
  227. lua_util.debugm(N, task,
  228. 'DNS RESPONSE: label=%1 results=%2 error=%3 rbl=%4',
  229. to_resolve, true, err, rbl.symbol)
  230. end
  231. if rbl.returncodes == nil and rbl.returnbits == nil and rbl.symbol ~= nil then
  232. insert_results(rbl.symbol)
  233. return
  234. end
  235. for _,result in ipairs(results) do
  236. local ipstr = result:to_string()
  237. lua_util.debugm(N, task, '%s DNS result %s', to_resolve, ipstr)
  238. local foundrc = false
  239. -- Check return codes
  240. if rbl.returnbits then
  241. local ipnum = result:to_number()
  242. for s,bits in pairs(rbl.returnbits) do
  243. for _,check_bit in ipairs(bits) do
  244. if bit.band(ipnum, check_bit) == check_bit then
  245. foundrc = true
  246. insert_results(s)
  247. -- Here, we continue with other bits
  248. end
  249. end
  250. end
  251. elseif rbl.returncodes then
  252. for s, codes in pairs(rbl.returncodes) do
  253. for _,v in ipairs(codes) do
  254. if string.find(ipstr, '^' .. v .. '$') then
  255. foundrc = true
  256. insert_results(s)
  257. break
  258. end
  259. end
  260. end
  261. end
  262. if not foundrc then
  263. if rbl.unknown and rbl.symbol then
  264. insert_results(rbl.symbol, ipstr)
  265. else
  266. lua_util.debugm(N, task, '%1 returned unknown result: %2',
  267. to_resolve, ipstr)
  268. end
  269. end
  270. end
  271. end
  272. local function gen_rbl_callback(rule)
  273. local function is_whitelisted(task, req, req_str, whitelist, what)
  274. if rule.ignore_whitelist then
  275. lua_util.debugm(N, task,
  276. 'ignore whitelisting checks to %s by %s: ignore whitelist is being set',
  277. req_str, rule.symbol)
  278. return false
  279. end
  280. if rule.whitelist then
  281. if rule.whitelist:get_key(req) then
  282. lua_util.debugm(N, task,
  283. 'whitelisted %s on %s',
  284. req_str, rule.symbol)
  285. return true
  286. end
  287. end
  288. -- Maybe whitelisted by some other rbl rule
  289. if whitelist then
  290. local wl = whitelist[req_str]
  291. if wl then
  292. lua_util.debugm(N, task,
  293. 'whitelisted request to %s by %s (%s) rbl rule (%s checked type, %s whitelist type)',
  294. req_str, wl.type, wl.symbol, what, wl.type)
  295. if wl.type == what then
  296. -- This was decided to be a bad idea as in case of whitelisting a request to blacklist
  297. -- is not even sent
  298. --task:adjust_result(wl.symbol, 0.0 / 0.0, rule.symbol)
  299. return true
  300. end
  301. end
  302. end
  303. return false
  304. end
  305. local function add_dns_request(task, req, forced, is_ip, requests_table, label, whitelist)
  306. local req_str = req
  307. if is_ip then
  308. req_str = tostring(req)
  309. end
  310. if whitelist and is_whitelisted(task, req, req_str, whitelist, label) then
  311. return
  312. end
  313. if is_ip then
  314. req = ip_to_rbl(req)
  315. end
  316. if requests_table[req] then
  317. -- Duplicate request
  318. local nreq = requests_table[req]
  319. if forced and not nreq.forced then
  320. nreq.forced = true
  321. end
  322. if not nreq.what[label] then
  323. nreq.what[label] = true
  324. end
  325. return true,nreq -- Duplicate
  326. else
  327. local nreq
  328. local resolve_ip = rule.resolve_ip and not is_ip
  329. if rule.process_script then
  330. local processed = rule.process_script(req, rule.rbl, task, resolve_ip)
  331. if processed then
  332. nreq = {
  333. forced = forced,
  334. n = processed,
  335. orig = req_str,
  336. resolve_ip = resolve_ip,
  337. what = {[label] = true},
  338. }
  339. requests_table[req] = nreq
  340. end
  341. else
  342. local to_resolve
  343. local origin = req
  344. if not resolve_ip then
  345. origin = maybe_make_hash(req, rule)
  346. to_resolve = string.format('%s.%s',
  347. origin,
  348. rule.rbl)
  349. else
  350. -- First, resolve origin stuff without hashing or anything
  351. to_resolve = origin
  352. end
  353. nreq = {
  354. forced = forced,
  355. n = to_resolve,
  356. orig = req_str,
  357. resolve_ip = resolve_ip,
  358. what = {[label] = true},
  359. }
  360. requests_table[req] = nreq
  361. end
  362. return false, nreq
  363. end
  364. end
  365. -- Here, we have functional approach: we form a pipeline of functions
  366. -- f1, f2, ... fn. Each function accepts task and return boolean value
  367. -- that allows to process pipeline further
  368. -- Each function in the pipeline can add something to `dns_req` vector as a side effect
  369. local function is_alive(_, _)
  370. if rule.monitored then
  371. if not rule.monitored:alive() then
  372. return false
  373. end
  374. end
  375. return true
  376. end
  377. local function check_required_symbols(task, _)
  378. if rule.require_symbols then
  379. return fun.all(function(sym) task:has_symbol(sym) end, rule.require_symbols)
  380. end
  381. return true
  382. end
  383. local function check_user(task, _)
  384. if task:get_user() then
  385. return false
  386. end
  387. return true
  388. end
  389. local function check_local(task, _)
  390. local ip = task:get_from_ip()
  391. if ip and not ip:is_valid() then
  392. ip = nil
  393. end
  394. if ip and ip:is_local() or is_excluded_ip(ip) then
  395. return false
  396. end
  397. return true
  398. end
  399. local function check_helo(task, requests_table, whitelist)
  400. local helo = task:get_helo()
  401. if not helo then
  402. return false
  403. end
  404. add_dns_request(task, helo, true, false, requests_table,
  405. 'helo', whitelist)
  406. return true
  407. end
  408. local function check_dkim(task, requests_table, whitelist)
  409. local das = task:get_symbol('DKIM_TRACE')
  410. local mime_from_domain
  411. if das and das[1] and das[1].options then
  412. if rule.dkim_match_from then
  413. -- We check merely mime from
  414. mime_from_domain = ((task:get_from('mime') or E)[1] or E).domain
  415. if mime_from_domain then
  416. local mime_from_domain_tld = rule.url_full_hostname and
  417. mime_from_domain or rspamd_util.get_tld(mime_from_domain)
  418. if rule.url_compose_map then
  419. mime_from_domain = rule.url_compose_map:process_url(task, mime_from_domain_tld, mime_from_domain)
  420. else
  421. mime_from_domain = mime_from_domain_tld
  422. end
  423. end
  424. end
  425. for _, d in ipairs(das[1].options) do
  426. local domain,result = d:match('^([^%:]*):([%+%-%~])$')
  427. -- We must ignore bad signatures, omg
  428. if domain and result and result == '+' then
  429. if rule.dkim_match_from then
  430. -- We check merely mime from
  431. local domain_tld = domain
  432. if not rule.dkim_domainonly then
  433. -- Adjust
  434. domain_tld = rspamd_util.get_tld(domain)
  435. if rule.url_compose_map then
  436. domain_tld = rule.url_compose_map:process_url(task, domain_tld, domain)
  437. elseif rule.url_full_hostname then
  438. domain_tld = domain
  439. end
  440. end
  441. if mime_from_domain and mime_from_domain == domain_tld then
  442. add_dns_request(task, domain_tld, true, false, requests_table,
  443. 'dkim', whitelist)
  444. end
  445. else
  446. if rule.dkim_domainonly then
  447. local domain_tld = rspamd_util.get_tld(domain)
  448. if rule.url_compose_map then
  449. domain_tld = rule.url_compose_map:process_url(task, domain_tld, domain)
  450. elseif rule.url_full_hostname then
  451. domain_tld = domain
  452. end
  453. add_dns_request(task, domain_tld,
  454. false, false, requests_table, 'dkim', whitelist)
  455. else
  456. add_dns_request(task, domain, false, false, requests_table,
  457. 'dkim', whitelist)
  458. end
  459. end
  460. end
  461. end
  462. end
  463. return true
  464. end
  465. local function check_urls(task, requests_table, whitelist)
  466. local esld_lim = 1
  467. if rule.url_compose_map then
  468. esld_lim = nil -- Avoid esld limit as we use custom composition rules
  469. end
  470. local ex_params = {
  471. task = task,
  472. limit = rule.requests_limit,
  473. ignore_redirected = true,
  474. ignore_ip = rule.no_ip,
  475. need_images = rule.images,
  476. need_emails = false,
  477. need_content = rule.content_urls or false,
  478. esld_limit = esld_lim,
  479. no_cache = true,
  480. }
  481. if not rule.urls then
  482. ex_params.flags_mode = 'explicit'
  483. ex_params.flags = {}
  484. if rule.content_urls then
  485. table.insert(ex_params.flags, 'content')
  486. end
  487. if rule.images then
  488. table.insert(ex_params.flags, 'image')
  489. end
  490. end
  491. local urls = lua_util.extract_specific_urls(ex_params)
  492. for _,u in ipairs(urls) do
  493. local flags = u:get_flags_num()
  494. if bit.band(flags, url_flag_bits.numeric) ~= 0 then
  495. -- For numeric urls we convert data to the ip address and
  496. -- reverse octets. See #3948 for details
  497. local to_resolve = u:get_host()
  498. local addr = rspamd_ip.from_string(to_resolve)
  499. if addr then
  500. to_resolve = table.concat(addr:inversed_str_octets(), ".")
  501. end
  502. add_dns_request(task, to_resolve, false,
  503. false, requests_table, 'url', whitelist)
  504. else
  505. local url_hostname = u:get_host()
  506. local url_tld = rule.url_full_hostname and url_hostname or u:get_tld()
  507. if rule.url_compose_map then
  508. url_tld = rule.url_compose_map:process_url(task, url_tld, url_hostname)
  509. end
  510. add_dns_request(task, url_tld, false,
  511. false, requests_table, 'url', whitelist)
  512. end
  513. end
  514. return true
  515. end
  516. local function check_from(task, requests_table, whitelist)
  517. local ip = task:get_from_ip()
  518. if not ip or not ip:is_valid() then
  519. return true
  520. end
  521. if (ip:get_version() == 6 and rule.ipv6) or
  522. (ip:get_version() == 4 and rule.ipv4) then
  523. add_dns_request(task, ip, true, true,
  524. requests_table, 'from',
  525. whitelist)
  526. end
  527. return true
  528. end
  529. local function check_received(task, requests_table, whitelist)
  530. local received = fun.filter(function(h)
  531. return not h['flags']['artificial']
  532. end, task:get_received_headers()):totable()
  533. local received_total = #received
  534. local check_conditions = gen_check_rcvd_conditions(rule, received_total)
  535. for pos,rh in ipairs(received) do
  536. if check_conditions(rh, pos) then
  537. add_dns_request(task, rh.real_ip, false, true,
  538. requests_table, 'received',
  539. whitelist)
  540. end
  541. end
  542. return true
  543. end
  544. local function check_rdns(task, requests_table, whitelist)
  545. local hostname = task:get_hostname()
  546. if hostname == nil or hostname == 'unknown' then
  547. return false
  548. end
  549. add_dns_request(task, hostname, true, false,
  550. requests_table, 'rdns', whitelist)
  551. return true
  552. end
  553. local function check_selector(task, requests_table, whitelist)
  554. for selector_label, selector in pairs(rule.selectors) do
  555. local res = selector(task)
  556. if res and type(res) == 'table' then
  557. for _,r in ipairs(res) do
  558. add_dns_request(task, r, false, false, requests_table,
  559. selector_label, whitelist)
  560. end
  561. elseif res then
  562. add_dns_request(task, res, false, false,
  563. requests_table, selector_label, whitelist)
  564. end
  565. end
  566. return true
  567. end
  568. local function check_email_table(task, email_tbl, requests_table, whitelist, what)
  569. lua_util.remove_email_aliases(email_tbl)
  570. email_tbl.domain = email_tbl.domain:lower()
  571. email_tbl.user = email_tbl.user:lower()
  572. if rule.emails_domainonly then
  573. add_dns_request(task, email_tbl.domain, false, false, requests_table,
  574. what, whitelist)
  575. else
  576. -- Also check WL for domain only
  577. if is_whitelisted(task,
  578. email_tbl.domain,
  579. email_tbl.domain,
  580. whitelist,
  581. what) then
  582. return
  583. end
  584. local delimiter = '.'
  585. if rule.emails_delimiter then
  586. delimiter = rule.emails_delimiter
  587. else
  588. if rule.hash then
  589. delimiter = '@'
  590. end
  591. end
  592. add_dns_request(task, string.format('%s%s%s',
  593. email_tbl.user, delimiter, email_tbl.domain), false, false,
  594. requests_table, what, whitelist)
  595. end
  596. end
  597. local function check_emails(task, requests_table, whitelist)
  598. local ex_params = {
  599. task = task,
  600. limit = rule.requests_limit,
  601. filter = function(u) return u:get_protocol() == 'mailto' end,
  602. need_emails = true,
  603. prefix = 'rbl_email'
  604. }
  605. if rule.emails_domainonly then
  606. if not rule.url_compose_map then
  607. ex_params.esld_limit = 1
  608. end
  609. ex_params.prefix = 'rbl_email_domainonly'
  610. end
  611. local emails = lua_util.extract_specific_urls(ex_params)
  612. for _,email in ipairs(emails) do
  613. local domain
  614. if rule.emails_domainonly and not rule.url_full_hostname then
  615. if rule.url_compose_map then
  616. domain = rule.url_compose_map:process_url(task, email:get_tld(), email:get_host())
  617. else
  618. domain = email:get_tld()
  619. end
  620. else
  621. domain = email:get_host()
  622. end
  623. local email_tbl = {
  624. domain = domain,
  625. user = email:get_user(),
  626. addr = tostring(email),
  627. }
  628. check_email_table(task, email_tbl, requests_table, whitelist, 'email')
  629. end
  630. return true
  631. end
  632. local function check_replyto(task, requests_table, whitelist)
  633. local function get_raw_header(name)
  634. return ((task:get_header_full(name) or {})[1] or {})['value']
  635. end
  636. local replyto = get_raw_header('Reply-To')
  637. if replyto then
  638. local rt = rspamd_util.parse_mail_address(replyto, task:get_mempool())
  639. lua_util.debugm(N, task, 'check replyto %s', rt[1])
  640. if rt and rt[1] and (rt[1].addr and #rt[1].addr > 0) then
  641. check_email_table(task, rt[1], requests_table, whitelist, 'replyto')
  642. end
  643. end
  644. return true
  645. end
  646. -- Create function pipeline depending on rbl settings
  647. local pipeline = {
  648. is_alive, -- check monitored status
  649. check_required_symbols -- if we have require_symbols then check those symbols
  650. }
  651. local description = {
  652. 'alive',
  653. }
  654. if rule.exclude_users then
  655. pipeline[#pipeline + 1] = check_user
  656. description[#description + 1] = 'user'
  657. end
  658. if rule.exclude_local or rule.exclude_private_ips then
  659. pipeline[#pipeline + 1] = check_local
  660. description[#description + 1] = 'local'
  661. end
  662. if rule.helo then
  663. pipeline[#pipeline + 1] = check_helo
  664. description[#description + 1] = 'helo'
  665. end
  666. if rule.dkim then
  667. pipeline[#pipeline + 1] = check_dkim
  668. description[#description + 1] = 'dkim'
  669. end
  670. if rule.emails then
  671. pipeline[#pipeline + 1] = check_emails
  672. description[#description + 1] = 'emails'
  673. end
  674. if rule.replyto then
  675. pipeline[#pipeline + 1] = check_replyto
  676. description[#description + 1] = 'replyto'
  677. end
  678. if rule.urls or rule.content_urls or rule.images then
  679. pipeline[#pipeline + 1] = check_urls
  680. description[#description + 1] = 'urls'
  681. end
  682. if rule.from then
  683. pipeline[#pipeline + 1] = check_from
  684. description[#description + 1] = 'ip'
  685. end
  686. if rule.received then
  687. pipeline[#pipeline + 1] = check_received
  688. description[#description + 1] = 'received'
  689. end
  690. if rule.rdns then
  691. pipeline[#pipeline + 1] = check_rdns
  692. description[#description + 1] = 'rdns'
  693. end
  694. if rule.selector then
  695. pipeline[#pipeline + 1] = check_selector
  696. description[#description + 1] = 'selector'
  697. end
  698. local callback_f = function(task)
  699. -- DNS requests to issue (might be hashed afterwards)
  700. local dns_req = {}
  701. local whitelist = task:cache_get('rbl_whitelisted') or {}
  702. local function gen_rbl_dns_callback(resolve_table_elt)
  703. return function(_, to_resolve, results, err)
  704. rbl_dns_process(task, rule, to_resolve, results, err, resolve_table_elt)
  705. end
  706. end
  707. -- Execute functions pipeline
  708. for i,f in ipairs(pipeline) do
  709. if not f(task, dns_req, whitelist) then
  710. lua_util.debugm(N, task,
  711. "skip rbl check: %s; pipeline condition %s returned false",
  712. rule.symbol, i)
  713. return
  714. end
  715. end
  716. -- Now check all DNS requests pending and emit them
  717. local r = task:get_resolver()
  718. -- Used for 2 passes ip resolution
  719. local resolved_req = {}
  720. local nresolved = 0
  721. -- This is called when doing resolve_ip phase...
  722. local function gen_rbl_ip_dns_callback(orig_resolve_table_elt)
  723. return function(_, _, results, err)
  724. if not err then
  725. for _,dns_res in ipairs(results) do
  726. -- Check if we have rspamd{ip} userdata
  727. if type(dns_res) == 'userdata' then
  728. -- Add result as an actual RBL request
  729. local label = next(orig_resolve_table_elt.what)
  730. local dup,nreq = add_dns_request(task, dns_res, false, true,
  731. resolved_req, label)
  732. -- Add original name
  733. if not dup then
  734. nreq.orig = nreq.orig .. ':' .. orig_resolve_table_elt.n
  735. end
  736. end
  737. end
  738. end
  739. nresolved = nresolved - 1
  740. if nresolved == 0 then
  741. -- Emit real RBL requests as there are no ip resolution requests
  742. for name, req in pairs(resolved_req) do
  743. if validate_dns(req.n) then
  744. lua_util.debugm(N, task, "rbl %s; resolve %s -> %s",
  745. rule.symbol, name, req.n)
  746. r:resolve_a({
  747. task = task,
  748. name = req.n,
  749. callback = gen_rbl_dns_callback(req),
  750. forced = req.forced
  751. })
  752. else
  753. rspamd_logger.warnx(task, 'cannot send invalid DNS request %s for %s',
  754. req.n, rule.symbol)
  755. end
  756. end
  757. end
  758. end
  759. end
  760. for name, req in pairs(dns_req) do
  761. if validate_dns(req.n) then
  762. lua_util.debugm(N, task, "rbl %s; resolve %s -> %s",
  763. rule.symbol, name, req.n)
  764. if req.resolve_ip then
  765. -- Deal with both ipv4 and ipv6
  766. -- Resolve names first
  767. if r:resolve_a({
  768. task = task,
  769. name = req.n,
  770. callback = gen_rbl_ip_dns_callback(req),
  771. forced = req.forced
  772. }) then
  773. nresolved = nresolved + 1
  774. end
  775. if r:resolve('aaaa', {
  776. task = task,
  777. name = req.n,
  778. callback = gen_rbl_ip_dns_callback(req),
  779. forced = req.forced
  780. }) then
  781. nresolved = nresolved + 1
  782. end
  783. else
  784. r:resolve_a({
  785. task = task,
  786. name = req.n,
  787. callback = gen_rbl_dns_callback(req),
  788. forced = req.forced
  789. })
  790. end
  791. else
  792. rspamd_logger.warnx(task, 'cannot send invalid DNS request %s for %s',
  793. req.n, rule.symbol)
  794. end
  795. end
  796. end
  797. return callback_f,string.format('checks: %s', table.concat(description, ','))
  798. end
  799. local function add_rbl(key, rbl, global_opts)
  800. if not rbl.symbol then
  801. rbl.symbol = key:upper()
  802. end
  803. local flags_tbl = {'no_squeeze'}
  804. if rbl.is_whitelist then
  805. flags_tbl[#flags_tbl + 1] = 'nice'
  806. end
  807. -- Check if rbl is available for empty tasks
  808. if not (rbl.emails or rbl.urls or rbl.dkim or rbl.received or rbl.selector or rbl.replyto) or
  809. rbl.is_empty then
  810. flags_tbl[#flags_tbl + 1] = 'empty'
  811. end
  812. if rbl.selector then
  813. rbl.selectors = {}
  814. if type(rbl.selector) ~= 'table' then
  815. rbl.selector = {['selector'] = rbl.selector}
  816. end
  817. for selector_label, selector in pairs(rbl.selector) do
  818. if known_selectors[selector] then
  819. lua_util.debugm(N, rspamd_config, 'reuse selector id %s',
  820. known_selectors[selector].id)
  821. rbl.selectors[selector_label] = known_selectors[selector].selector
  822. else
  823. if type(rbl.selector_flatten) ~= 'boolean' then
  824. -- Fail-safety
  825. rbl.selector_flatten = true
  826. end
  827. local sel = selectors.create_selector_closure(rspamd_config, selector, '',
  828. rbl.selector_flatten)
  829. if not sel then
  830. rspamd_logger.errx('invalid selector for rbl rule %s: %s', key, selector)
  831. return false
  832. end
  833. rbl.selector = sel
  834. known_selectors[selector] = {
  835. selector = sel,
  836. id = #lua_util.keys(known_selectors) + 1,
  837. }
  838. rbl.selectors[selector_label] = known_selectors[selector].selector
  839. end
  840. end
  841. end
  842. if rbl.process_script then
  843. local ret, f = lua_util.callback_from_string(rbl.process_script)
  844. if ret then
  845. rbl.process_script = f
  846. else
  847. rspamd_logger.errx(rspamd_config,
  848. 'invalid process script for rbl rule %s: %s; %s',
  849. key, rbl.process_script, f)
  850. return false
  851. end
  852. end
  853. if rbl.whitelist then
  854. local def_type = 'set'
  855. if rbl.from or rbl.received then
  856. def_type = 'radix'
  857. end
  858. rbl.whitelist = lua_maps.map_add_from_ucl(rbl.whitelist, def_type,
  859. 'RBL whitelist for ' .. rbl.symbol)
  860. rspamd_logger.infox(rspamd_config, 'added %s whitelist for RBL %s',
  861. def_type, rbl.symbol)
  862. end
  863. if rbl.url_compose_map then
  864. local lua_urls_compose = require "lua_urls_compose"
  865. rbl.url_compose_map = lua_urls_compose.add_composition_map(rspamd_config, rbl.url_compose_map)
  866. if rbl.url_compose_map then
  867. rspamd_logger.infox(rspamd_config, 'added url composition map for RBL %s',
  868. rbl.symbol)
  869. end
  870. end
  871. if not rbl.whitelist and global_opts.url_whitelist and
  872. (rbl.urls or rbl.emails or rbl.dkim or rbl.replyto) and
  873. not (rbl.from or rbl.received) then
  874. local def_type = 'set'
  875. rbl.whitelist = lua_maps.map_add_from_ucl(global_opts.url_whitelist, def_type,
  876. 'RBL url whitelist for ' .. rbl.symbol)
  877. rspamd_logger.infox(rspamd_config, 'added URL whitelist for RBL %s',
  878. rbl.symbol)
  879. end
  880. local callback,description = gen_rbl_callback(rbl)
  881. if callback then
  882. local id
  883. if rbl.symbols_prefixes then
  884. id = rspamd_config:register_symbol{
  885. type = 'callback',
  886. callback = callback,
  887. name = rbl.symbol .. '_CHECK',
  888. flags = table.concat(flags_tbl, ',')
  889. }
  890. for _,prefix in pairs(rbl.symbols_prefixes) do
  891. -- For unknown results...
  892. rspamd_config:register_symbol{
  893. type = 'virtual',
  894. parent = id,
  895. group = 'rbl',
  896. score = 0,
  897. name = prefix .. '_' .. rbl.symbol,
  898. }
  899. end
  900. if not (rbl.is_whitelist or rbl.ignore_whitelist) then
  901. table.insert(black_symbols, rbl.symbol .. '_CHECK')
  902. else
  903. lua_util.debugm(N, rspamd_config, 'rule %s ignores whitelists: rbl.is_whitelist = %s, ' ..
  904. 'rbl.ignore_whitelist = %s',
  905. rbl.symbol, rbl.is_whitelist, rbl.ignore_whitelist)
  906. end
  907. else
  908. id = rspamd_config:register_symbol{
  909. type = 'callback',
  910. callback = callback,
  911. name = rbl.symbol,
  912. group = 'rbl',
  913. score = 0,
  914. flags = table.concat(flags_tbl, ',')
  915. }
  916. if not (rbl.is_whitelist or rbl.ignore_whitelist) then
  917. table.insert(black_symbols, rbl.symbol)
  918. else
  919. lua_util.debugm(N, rspamd_config, 'rule %s ignores whitelists: rbl.is_whitelist = %s, ' ..
  920. 'rbl.ignore_whitelist = %s',
  921. rbl.symbol, rbl.is_whitelist, rbl.ignore_whitelist)
  922. end
  923. end
  924. rspamd_logger.infox(rspamd_config, 'added rbl rule %s: %s',
  925. rbl.symbol, description)
  926. lua_util.debugm(N, rspamd_config, 'rule dump for %s: %s',
  927. rbl.symbol, rbl)
  928. if rbl.dkim then
  929. rspamd_config:register_dependency(rbl.symbol, 'DKIM_CHECK')
  930. end
  931. if rbl.require_symbols then
  932. for _,dep in ipairs(rbl.require_symbols) do
  933. rspamd_config:register_dependency(rbl.symbol, dep)
  934. end
  935. end
  936. -- Failure symbol
  937. rspamd_config:register_symbol{
  938. type = 'virtual',
  939. flags = 'nostat',
  940. name = rbl.symbol .. '_FAIL',
  941. parent = id,
  942. score = 0.0,
  943. }
  944. local function process_return_code(suffix)
  945. local function process_specific_suffix(s)
  946. if s ~= rbl.symbol then
  947. -- hack
  948. rspamd_config:register_symbol{
  949. type = 'virtual',
  950. parent = id,
  951. name = s,
  952. group = 'rbl',
  953. score = 0,
  954. }
  955. end
  956. if rbl.is_whitelist then
  957. if rbl.whitelist_exception then
  958. local found_exception = false
  959. for _, e in ipairs(rbl.whitelist_exception) do
  960. if e == s then
  961. found_exception = true
  962. break
  963. end
  964. end
  965. if not found_exception then
  966. table.insert(white_symbols, s)
  967. end
  968. else
  969. table.insert(white_symbols, s)
  970. end
  971. else
  972. if not rbl.ignore_whitelist then
  973. table.insert(black_symbols, s)
  974. end
  975. end
  976. end
  977. if rbl.symbols_prefixes then
  978. for _,prefix in pairs(rbl.symbols_prefixes) do
  979. process_specific_suffix(prefix .. '_' .. suffix)
  980. end
  981. else
  982. process_specific_suffix(suffix)
  983. end
  984. end
  985. if rbl.returncodes then
  986. for s,_ in pairs(rbl.returncodes) do
  987. process_return_code(s)
  988. end
  989. end
  990. if rbl.returnbits then
  991. for s,_ in pairs(rbl.returnbits) do
  992. process_return_code(s)
  993. end
  994. end
  995. -- Process monitored
  996. if not rbl.disable_monitoring then
  997. if not monitored_addresses[rbl.rbl] then
  998. monitored_addresses[rbl.rbl] = true
  999. rbl.monitored = rspamd_config:register_monitored(rbl.rbl, 'dns',
  1000. get_monitored(rbl))
  1001. end
  1002. end
  1003. return true
  1004. end
  1005. return false
  1006. end
  1007. -- Configuration
  1008. local opts = rspamd_config:get_all_opt(N)
  1009. if not (opts and type(opts) == 'table') then
  1010. rspamd_logger.infox(rspamd_config, 'Module is unconfigured')
  1011. lua_util.disable_module(N, "config")
  1012. return
  1013. end
  1014. -- Plugin defaults should not be changed - override these in config
  1015. -- New defaults should not alter behaviour
  1016. opts = lua_util.override_defaults(rbl_common.default_options, opts)
  1017. if opts.rules and opts.rbls then
  1018. -- Common issue :(
  1019. rspamd_logger.infox(rspamd_config, 'merging `rules` and `rbls` keys for compatibility')
  1020. opts.rbls = lua_util.override_defaults(opts.rbls, opts.rules)
  1021. end
  1022. if(opts['local_exclude_ip_map'] ~= nil) then
  1023. local_exclusions = lua_maps.map_add(N, 'local_exclude_ip_map', 'radix',
  1024. 'RBL exclusions map')
  1025. end
  1026. for key,rbl in pairs(opts.rbls ) do
  1027. if type(rbl) ~= 'table' or rbl.disabled == true or rbl.enabled == false then
  1028. rspamd_logger.infox(rspamd_config, 'disable rbl "%s"', key)
  1029. else
  1030. -- Aliases
  1031. if type(rbl.ignore_default) == 'boolean' then
  1032. rbl.ignore_defaults = rbl.ignore_default
  1033. end
  1034. if type(rbl.ignore_whitelists) == 'boolean' then
  1035. rbl.ignore_whitelist = rbl.ignore_whitelists
  1036. end
  1037. -- Propagate default options from opts to rule
  1038. if not rbl.ignore_defaults then
  1039. for default_opt_key,_ in pairs(rbl_common.default_options) do
  1040. local rbl_opt = default_opt_key:sub(#('default_') + 1)
  1041. if rbl[rbl_opt] == nil then
  1042. rbl[rbl_opt] = opts[default_opt_key]
  1043. end
  1044. end
  1045. end
  1046. if not rbl.requests_limit then
  1047. rbl.requests_limit = rspamd_config:get_dns_max_requests()
  1048. end
  1049. local res,err = rbl_common.rule_schema:transform(rbl)
  1050. if not res then
  1051. rspamd_logger.errx(rspamd_config, 'invalid config for %s: %s, RBL is DISABLED',
  1052. key, err)
  1053. else
  1054. res = rbl_common.convert_checks(res)
  1055. -- Aliases
  1056. if res.return_codes then res.returncodes = res.return_codes end
  1057. if res.return_bits then res.returnbits = res.return_bits end
  1058. if not res then
  1059. rspamd_logger.errx(rspamd_config, 'invalid config for %s: %s, RBL is DISABLED',
  1060. key, err)
  1061. else
  1062. add_rbl(key, res, opts)
  1063. end
  1064. end
  1065. end -- rbl.enabled
  1066. end
  1067. -- We now create two symbols:
  1068. -- * RBL_CALLBACK_WHITE that depends on all symbols white
  1069. -- * RBL_CALLBACK that depends on all symbols black to participate in depends chains
  1070. local function rbl_callback_white(task)
  1071. local whitelisted_elements = {}
  1072. for _, w in ipairs(white_symbols) do
  1073. local ws = task:get_symbol(w)
  1074. if ws and ws[1] then
  1075. ws = ws[1]
  1076. if not ws.options then ws.options = {} end
  1077. for _,opt in ipairs(ws.options) do
  1078. local elt,what = opt:match('^([^:]+):([^:]+)')
  1079. lua_util.debugm(N, task,'found whitelist from %s: %s(%s)', w,
  1080. elt, what)
  1081. if elt and what then
  1082. whitelisted_elements[elt] = {
  1083. type = what,
  1084. symbol = w,
  1085. }
  1086. end
  1087. end
  1088. end
  1089. end
  1090. task:cache_set('rbl_whitelisted', whitelisted_elements)
  1091. lua_util.debugm(N, task, "finished rbl whitelists processing")
  1092. end
  1093. local function rbl_callback_fin(task)
  1094. -- Do nothing
  1095. lua_util.debugm(N, task, "finished rbl processing")
  1096. end
  1097. rspamd_config:register_symbol{
  1098. type = 'callback',
  1099. callback = rbl_callback_white,
  1100. name = 'RBL_CALLBACK_WHITE',
  1101. flags = 'nice,empty,no_squeeze'
  1102. }
  1103. rspamd_config:register_symbol{
  1104. type = 'callback',
  1105. callback = rbl_callback_fin,
  1106. name = 'RBL_CALLBACK',
  1107. flags = 'empty,no_squeeze'
  1108. }
  1109. for _, w in ipairs(white_symbols) do
  1110. rspamd_config:register_dependency('RBL_CALLBACK_WHITE', w)
  1111. end
  1112. for _, b in ipairs(black_symbols) do
  1113. rspamd_config:register_dependency(b, 'RBL_CALLBACK_WHITE')
  1114. rspamd_config:register_dependency('RBL_CALLBACK', b)
  1115. end