Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

lua_spf.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614
  1. /*-
  2. * Copyright 2019 Vsevolod Stakhov
  3. *
  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. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /**
  17. * @file lua_spf.c
  18. * This module exports spf functions to Lua
  19. */
  20. #include "lua_common.h"
  21. #include "libserver/spf.h"
  22. #include "libutil/ref.h"
  23. #define SPF_RECORD_CLASS "rspamd{spf_record}"
  24. LUA_FUNCTION_DEF (spf, resolve);
  25. LUA_FUNCTION_DEF (spf, config);
  26. LUA_FUNCTION_DEF (spf_record, check_ip);
  27. LUA_FUNCTION_DEF (spf_record, dtor);
  28. LUA_FUNCTION_DEF (spf_record, get_domain);
  29. LUA_FUNCTION_DEF (spf_record, get_elts);
  30. LUA_FUNCTION_DEF (spf_record, get_ttl);
  31. LUA_FUNCTION_DEF (spf_record, get_timestamp);
  32. LUA_FUNCTION_DEF (spf_record, get_digest);
  33. static luaL_reg rspamd_spf_f[] = {
  34. LUA_INTERFACE_DEF (spf, resolve),
  35. LUA_INTERFACE_DEF (spf, config),
  36. {NULL, NULL},
  37. };
  38. static luaL_reg rspamd_spf_record_m[] = {
  39. LUA_INTERFACE_DEF (spf_record, check_ip),
  40. LUA_INTERFACE_DEF (spf_record, get_domain),
  41. LUA_INTERFACE_DEF (spf_record, get_ttl),
  42. LUA_INTERFACE_DEF (spf_record, get_digest),
  43. LUA_INTERFACE_DEF (spf_record, get_elts),
  44. LUA_INTERFACE_DEF (spf_record, get_timestamp),
  45. {"__gc", lua_spf_record_dtor},
  46. {NULL, NULL},
  47. };
  48. struct rspamd_lua_spf_cbdata {
  49. struct rspamd_task *task;
  50. lua_State *L;
  51. struct rspamd_symcache_item *item;
  52. gint cbref;
  53. ref_entry_t ref;
  54. };
  55. static gint
  56. lua_load_spf (lua_State * L)
  57. {
  58. lua_newtable (L);
  59. /* Create integer arguments to check SPF results */
  60. lua_newtable (L);
  61. lua_pushinteger (L, SPF_FAIL);
  62. lua_setfield (L, -2, "fail");
  63. lua_pushinteger (L, SPF_PASS);
  64. lua_setfield (L, -2, "pass");
  65. lua_pushinteger (L, SPF_NEUTRAL);
  66. lua_setfield (L, -2, "neutral");
  67. lua_pushinteger (L, SPF_SOFT_FAIL);
  68. lua_setfield (L, -2, "soft_fail");
  69. lua_setfield (L, -2, "policy");
  70. /* Flags stuff */
  71. lua_newtable (L);
  72. lua_pushinteger (L, RSPAMD_SPF_RESOLVED_TEMP_FAILED);
  73. lua_setfield (L, -2, "temp_fail");
  74. lua_pushinteger (L, RSPAMD_SPF_RESOLVED_NA);
  75. lua_setfield (L, -2, "na");
  76. lua_pushinteger (L, RSPAMD_SPF_RESOLVED_PERM_FAILED);
  77. lua_setfield (L, -2, "perm_fail");
  78. lua_pushinteger (L, RSPAMD_SPF_FLAG_CACHED);
  79. lua_setfield (L, -2, "cached");
  80. lua_setfield (L, -2, "flags");
  81. luaL_register (L, NULL, rspamd_spf_f);
  82. return 1;
  83. }
  84. void luaopen_spf (lua_State *L)
  85. {
  86. rspamd_lua_new_class (L, SPF_RECORD_CLASS, rspamd_spf_record_m);
  87. lua_pop (L, 1); /* No need in metatable... */
  88. rspamd_lua_add_preload (L, "rspamd_spf", lua_load_spf);
  89. lua_settop (L, 0);
  90. }
  91. static void
  92. lua_spf_push_result (struct rspamd_lua_spf_cbdata *cbd, gint code_flags,
  93. struct spf_resolved *resolved, const gchar *err)
  94. {
  95. g_assert (cbd != NULL);
  96. REF_RETAIN (cbd);
  97. lua_pushcfunction (cbd->L, &rspamd_lua_traceback);
  98. gint err_idx = lua_gettop (cbd->L);
  99. lua_rawgeti (cbd->L, LUA_REGISTRYINDEX, cbd->cbref);
  100. if (resolved) {
  101. struct spf_resolved **presolved;
  102. presolved = lua_newuserdata (cbd->L, sizeof (*presolved));
  103. rspamd_lua_setclass (cbd->L, SPF_RECORD_CLASS, -1);
  104. *presolved = spf_record_ref (resolved);
  105. }
  106. else {
  107. lua_pushnil (cbd->L);
  108. }
  109. lua_pushinteger (cbd->L, code_flags);
  110. if (err) {
  111. lua_pushstring (cbd->L, err);
  112. }
  113. else {
  114. lua_pushnil (cbd->L);
  115. }
  116. if (lua_pcall (cbd->L, 3, 0, err_idx) != 0) {
  117. struct rspamd_task *task = cbd->task;
  118. msg_err_task ("cannot call callback function for spf: %s",
  119. lua_tostring (cbd->L, -1));
  120. }
  121. lua_settop (cbd->L, err_idx - 1);
  122. REF_RELEASE (cbd);
  123. }
  124. static void
  125. lua_spf_dtor (struct rspamd_lua_spf_cbdata *cbd)
  126. {
  127. if (cbd) {
  128. luaL_unref (cbd->L, LUA_REGISTRYINDEX, cbd->cbref);
  129. if (cbd->item) {
  130. rspamd_symcache_item_async_dec_check (cbd->task, cbd->item,
  131. "lua_spf");
  132. }
  133. }
  134. }
  135. static void
  136. spf_lua_lib_callback (struct spf_resolved *record, struct rspamd_task *task,
  137. gpointer ud)
  138. {
  139. struct rspamd_lua_spf_cbdata *cbd = (struct rspamd_lua_spf_cbdata *)ud;
  140. if (record) {
  141. if ((record->flags & RSPAMD_SPF_RESOLVED_NA)) {
  142. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_NA, NULL,
  143. "no SPF record");
  144. }
  145. else if (record->elts->len == 0) {
  146. if (record->flags & RSPAMD_SPF_RESOLVED_PERM_FAILED) {
  147. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_PERM_FAILED, NULL,
  148. "bad SPF record");
  149. }
  150. else if ((record->flags & RSPAMD_SPF_RESOLVED_TEMP_FAILED)) {
  151. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_TEMP_FAILED, NULL,
  152. "temporary DNS error");
  153. }
  154. else {
  155. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_PERM_FAILED, NULL,
  156. "empty SPF record");
  157. }
  158. }
  159. else if (record->domain) {
  160. spf_record_ref (record);
  161. lua_spf_push_result (cbd, record->flags, record, NULL);
  162. spf_record_unref (record);
  163. }
  164. else {
  165. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_PERM_FAILED, NULL,
  166. "internal error: non empty record for no domain");
  167. }
  168. }
  169. else {
  170. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_PERM_FAILED, NULL,
  171. "internal error: no record");
  172. }
  173. REF_RELEASE (cbd);
  174. }
  175. /***
  176. * @function rspamd_spf.resolve(task, callback)
  177. * Resolves SPF credentials for a task
  178. * @param {rspamd_task} task task
  179. * @param {function} callback callback that is called on spf resolution
  180. */
  181. gint
  182. lua_spf_resolve (lua_State * L)
  183. {
  184. struct rspamd_task *task = lua_check_task (L, 1);
  185. if (task && lua_isfunction (L, 2)) {
  186. struct rspamd_lua_spf_cbdata *cbd = rspamd_mempool_alloc0 (task->task_pool,
  187. sizeof (*cbd));
  188. struct rspamd_spf_cred *spf_cred;
  189. cbd->task = task;
  190. cbd->L = L;
  191. lua_pushvalue (L, 2);
  192. cbd->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
  193. /* TODO: make it as an optional parameter */
  194. spf_cred = rspamd_spf_get_cred (task);
  195. cbd->item = rspamd_symcache_get_cur_item (task);
  196. if (cbd->item) {
  197. rspamd_symcache_item_async_inc (task, cbd->item, "lua_spf");
  198. }
  199. REF_INIT_RETAIN (cbd, lua_spf_dtor);
  200. if (!rspamd_spf_resolve (task, spf_lua_lib_callback, cbd, spf_cred)) {
  201. msg_info_task ("cannot make spf request for %s",
  202. spf_cred ? spf_cred->domain : "empty domain");
  203. if (spf_cred) {
  204. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_TEMP_FAILED,
  205. NULL, "DNS failed");
  206. }
  207. else {
  208. lua_spf_push_result (cbd, RSPAMD_SPF_RESOLVED_NA,
  209. NULL, "No domain");
  210. }
  211. REF_RELEASE (cbd);
  212. }
  213. }
  214. else {
  215. return luaL_error (L, "invalid arguments");
  216. }
  217. return 0;
  218. }
  219. static gint
  220. lua_spf_record_dtor (lua_State *L)
  221. {
  222. struct spf_resolved *record =
  223. * (struct spf_resolved **)rspamd_lua_check_udata (L, 1,
  224. SPF_RECORD_CLASS);
  225. if (record) {
  226. spf_record_unref (record);
  227. }
  228. return 0;
  229. }
  230. static void
  231. lua_spf_push_spf_addr (lua_State *L, struct spf_addr *addr)
  232. {
  233. gchar *addr_mask;
  234. lua_createtable (L, 0, 4);
  235. lua_pushinteger (L, addr->mech);
  236. lua_setfield (L, -2, "result");
  237. lua_pushinteger (L, addr->flags);
  238. lua_setfield (L, -2, "flags");
  239. if (addr->spf_string) {
  240. lua_pushstring (L, addr->spf_string);
  241. lua_setfield (L, -2, "str");
  242. }
  243. addr_mask = spf_addr_mask_to_string (addr);
  244. if (addr_mask) {
  245. lua_pushstring (L, addr_mask);
  246. lua_setfield (L, -2, "addr");
  247. g_free (addr_mask);
  248. }
  249. }
  250. static gint
  251. spf_check_element (lua_State *L, struct spf_resolved *rec, struct spf_addr *addr,
  252. struct rspamd_lua_ip *ip)
  253. {
  254. gboolean res = FALSE;
  255. const guint8 *s, *d;
  256. guint af, mask, bmask, addrlen;
  257. if (addr->flags & RSPAMD_SPF_FLAG_TEMPFAIL) {
  258. /* Ignore failed addresses */
  259. return -1;
  260. }
  261. af = rspamd_inet_address_get_af (ip->addr);
  262. /* Basic comparing algorithm */
  263. if (((addr->flags & RSPAMD_SPF_FLAG_IPV6) && af == AF_INET6) ||
  264. ((addr->flags & RSPAMD_SPF_FLAG_IPV4) && af == AF_INET)) {
  265. d = rspamd_inet_address_get_hash_key (ip->addr, &addrlen);
  266. if (af == AF_INET6) {
  267. s = (const guint8 *)addr->addr6;
  268. mask = addr->m.dual.mask_v6;
  269. }
  270. else {
  271. s = (const guint8 *)addr->addr4;
  272. mask = addr->m.dual.mask_v4;
  273. }
  274. /* Compare the first bytes */
  275. bmask = mask / CHAR_BIT;
  276. if (mask > addrlen * CHAR_BIT) {
  277. /* XXX: add logging */
  278. }
  279. else if (memcmp (s, d, bmask) == 0) {
  280. if (bmask * CHAR_BIT < mask) {
  281. /* Compare the remaining bits */
  282. s += bmask;
  283. d += bmask;
  284. mask = (0xff << (CHAR_BIT - (mask - bmask * 8))) & 0xff;
  285. if ((*s & mask) == (*d & mask)) {
  286. res = TRUE;
  287. }
  288. }
  289. else {
  290. res = TRUE;
  291. }
  292. }
  293. }
  294. else {
  295. if (addr->flags & RSPAMD_SPF_FLAG_ANY) {
  296. res = TRUE;
  297. }
  298. else {
  299. res = FALSE;
  300. }
  301. }
  302. if (res) {
  303. if (addr->flags & RSPAMD_SPF_FLAG_ANY) {
  304. if (rec->flags & RSPAMD_SPF_RESOLVED_PERM_FAILED) {
  305. lua_pushboolean (L, false);
  306. lua_pushinteger (L, RSPAMD_SPF_RESOLVED_PERM_FAILED);
  307. lua_pushfstring (L, "%cany", spf_mech_char (addr->mech));
  308. }
  309. else if (rec->flags & RSPAMD_SPF_RESOLVED_TEMP_FAILED) {
  310. lua_pushboolean (L, false);
  311. lua_pushinteger (L, RSPAMD_SPF_RESOLVED_TEMP_FAILED);
  312. lua_pushfstring (L, "%cany", spf_mech_char (addr->mech));
  313. }
  314. else {
  315. lua_pushboolean (L, true);
  316. lua_pushinteger (L, addr->mech);
  317. lua_spf_push_spf_addr (L, addr);
  318. }
  319. }
  320. else {
  321. lua_pushboolean (L, true);
  322. lua_pushinteger (L, addr->mech);
  323. lua_spf_push_spf_addr (L, addr);
  324. }
  325. return 3;
  326. }
  327. return -1;
  328. }
  329. /***
  330. * @method rspamd_spf_record:check_ip(ip)
  331. * Checks the processed record versus a specific IP address. This function
  332. * returns 3 values normally:
  333. * 1. Boolean check result
  334. * 2. If result is `false` then the second value is the error flag (e.g. rspamd_spf.flags.temp_fail), otherwise it will be an SPF method
  335. * 3. If result is `false` then this will be an error string, otherwise - an SPF string (e.g. `mx` or `ip4:x.y.z.1`)
  336. * @param {rspamd_ip|string} ip address
  337. * @return {result,flag_or_policy,error_or_addr} - triplet
  338. */
  339. static gint
  340. lua_spf_record_check_ip (lua_State *L)
  341. {
  342. struct spf_resolved *record =
  343. * (struct spf_resolved **)rspamd_lua_check_udata (L, 1,
  344. SPF_RECORD_CLASS);
  345. struct rspamd_lua_ip *ip = NULL;
  346. gint nres = 0;
  347. gboolean need_free_ip = FALSE;
  348. if (lua_type (L, 2) == LUA_TUSERDATA) {
  349. ip = lua_check_ip (L, 2);
  350. }
  351. else if (lua_type (L, 2) == LUA_TSTRING) {
  352. const gchar *ip_str;
  353. gsize iplen;
  354. ip = g_malloc0 (sizeof (struct rspamd_lua_ip));
  355. ip_str = lua_tolstring (L, 2, &iplen);
  356. if (!rspamd_parse_inet_address (&ip->addr,
  357. ip_str, iplen, RSPAMD_INET_ADDRESS_PARSE_DEFAULT)) {
  358. g_free (ip);
  359. ip = NULL;
  360. }
  361. else {
  362. need_free_ip = TRUE;
  363. }
  364. }
  365. if (record && ip && ip->addr) {
  366. for (guint i = 0; i < record->elts->len; i ++) {
  367. struct spf_addr *addr = &g_array_index (record->elts, struct spf_addr, i);
  368. if ((nres = spf_check_element (L, record, addr, ip)) > 0) {
  369. if (need_free_ip) {
  370. g_free (ip);
  371. }
  372. return nres;
  373. }
  374. }
  375. }
  376. else {
  377. if (need_free_ip) {
  378. g_free (ip);
  379. }
  380. return luaL_error (L, "invalid arguments");
  381. }
  382. if (need_free_ip) {
  383. g_free (ip);
  384. }
  385. /* If we are here it means that there is no ALL record */
  386. /*
  387. * According to https://tools.ietf.org/html/rfc7208#section-4.7 it means
  388. * SPF neutral
  389. */
  390. struct spf_addr fake_all;
  391. fake_all.mech = SPF_NEUTRAL;
  392. fake_all.flags = RSPAMD_SPF_FLAG_ANY;
  393. fake_all.spf_string = "all";
  394. lua_pushboolean (L, true);
  395. lua_pushinteger (L, SPF_NEUTRAL);
  396. lua_spf_push_spf_addr (L, &fake_all);
  397. return 3;
  398. }
  399. /***
  400. * @method rspamd_spf_record:get_domain()
  401. * Returns domain for the specific spf record
  402. */
  403. static gint
  404. lua_spf_record_get_domain (lua_State *L)
  405. {
  406. struct spf_resolved *record =
  407. *(struct spf_resolved **) rspamd_lua_check_udata (L, 1,
  408. SPF_RECORD_CLASS);
  409. if (record) {
  410. lua_pushstring (L, record->domain);
  411. }
  412. else {
  413. return luaL_error (L, "invalid arguments");
  414. }
  415. return 1;
  416. }
  417. /***
  418. * @method rspamd_spf_record:get_ttl()
  419. * Returns ttl for the specific spf record
  420. */
  421. static gint
  422. lua_spf_record_get_ttl (lua_State *L)
  423. {
  424. struct spf_resolved *record =
  425. *(struct spf_resolved **) rspamd_lua_check_udata (L, 1,
  426. SPF_RECORD_CLASS);
  427. if (record) {
  428. lua_pushinteger (L, record->ttl);
  429. }
  430. else {
  431. return luaL_error (L, "invalid arguments");
  432. }
  433. return 1;
  434. }
  435. /***
  436. * @method rspamd_spf_record:get_timestamp()
  437. * Returns ttl for the specific spf record
  438. */
  439. static gint
  440. lua_spf_record_get_timestamp (lua_State *L)
  441. {
  442. struct spf_resolved *record =
  443. *(struct spf_resolved **) rspamd_lua_check_udata (L, 1,
  444. SPF_RECORD_CLASS);
  445. if (record) {
  446. lua_pushnumber (L, record->timestamp);
  447. }
  448. else {
  449. return luaL_error (L, "invalid arguments");
  450. }
  451. return 1;
  452. }
  453. /***
  454. * @method rspamd_spf_record:get_digest()
  455. * Returns string hex representation of the record digest (fast hash function)
  456. */
  457. static gint
  458. lua_spf_record_get_digest (lua_State *L)
  459. {
  460. struct spf_resolved *record =
  461. *(struct spf_resolved **) rspamd_lua_check_udata (L, 1,
  462. SPF_RECORD_CLASS);
  463. if (record) {
  464. gchar hexbuf[64];
  465. rspamd_snprintf (hexbuf, sizeof (hexbuf), "%xuL", record->digest);
  466. lua_pushstring (L, hexbuf);
  467. }
  468. else {
  469. return luaL_error (L, "invalid arguments");
  470. }
  471. return 1;
  472. }
  473. /***
  474. * @method rspamd_spf_record:get_elts()
  475. * Returns a list of all elements in an SPF record. Each element is a table with the
  476. * following fields:
  477. *
  478. * - result - mech flag from rspamd_spf.results
  479. * - flags - all flags
  480. * - addr - address and mask as a string
  481. * - str - string representation (if available)
  482. */
  483. static gint
  484. lua_spf_record_get_elts (lua_State *L)
  485. {
  486. struct spf_resolved *record =
  487. *(struct spf_resolved **) rspamd_lua_check_udata (L, 1,
  488. SPF_RECORD_CLASS);
  489. if (record) {
  490. guint i;
  491. struct spf_addr *addr;
  492. lua_createtable (L, record->elts->len, 0);
  493. for (i = 0; i < record->elts->len; i ++) {
  494. addr = (struct spf_addr *)&g_array_index (record->elts,
  495. struct spf_addr, i);
  496. lua_spf_push_spf_addr (L, addr);
  497. lua_rawseti (L, -2, i + 1);
  498. }
  499. }
  500. else {
  501. return luaL_error (L, "invalid arguments");
  502. }
  503. return 1;
  504. }
  505. /***
  506. * @function rspamd_spf.config(object)
  507. * Configures SPF library according to the UCL config
  508. * @param {table} object configuration object
  509. */
  510. gint
  511. lua_spf_config (lua_State * L)
  512. {
  513. ucl_object_t *config_obj = ucl_object_lua_import (L, 1);
  514. if (config_obj) {
  515. spf_library_config (config_obj);
  516. ucl_object_unref (config_obj); /* As we copy data all the time */
  517. }
  518. else {
  519. return luaL_error (L, "invalid arguments");
  520. }
  521. return 0;
  522. }