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.

dkim_check.c 40KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620
  1. /*
  2. * Copyright 2024 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. /***MODULE:dkim
  17. * rspamd module that checks dkim records of incoming email
  18. *
  19. * Allowed options:
  20. * - symbol_allow (string): symbol to insert in case of allow (default: 'R_DKIM_ALLOW')
  21. * - symbol_reject (string): symbol to insert (default: 'R_DKIM_REJECT')
  22. * - symbol_tempfail (string): symbol to insert in case of temporary fail (default: 'R_DKIM_TEMPFAIL')
  23. * - symbol_permfail (string): symbol to insert in case of permanent failure (default: 'R_DKIM_PERMFAIL')
  24. * - symbol_na (string): symbol to insert in case of no signing (default: 'R_DKIM_NA')
  25. * - whitelist (map): map of whitelisted networks
  26. * - domains (map): map of domains to check
  27. * - strict_multiplier (number): multiplier for strict domains
  28. * - time_jitter (number): jitter in seconds to allow time diff while checking
  29. * - trusted_only (flag): check signatures only for domains in 'domains' map
  30. */
  31. #include "config.h"
  32. #include "libmime/message.h"
  33. #include "libserver/dkim.h"
  34. #include "libutil/hash.h"
  35. #include "libserver/maps/map.h"
  36. #include "libserver/maps/map_helpers.h"
  37. #include "rspamd.h"
  38. #include "utlist.h"
  39. #include "unix-std.h"
  40. #include "lua/lua_common.h"
  41. #include "libserver/mempool_vars_internal.h"
  42. #define DEFAULT_SYMBOL_REJECT "R_DKIM_REJECT"
  43. #define DEFAULT_SYMBOL_TEMPFAIL "R_DKIM_TEMPFAIL"
  44. #define DEFAULT_SYMBOL_ALLOW "R_DKIM_ALLOW"
  45. #define DEFAULT_SYMBOL_NA "R_DKIM_NA"
  46. #define DEFAULT_SYMBOL_PERMFAIL "R_DKIM_PERMFAIL"
  47. #define DEFAULT_CACHE_SIZE 2048
  48. #define DEFAULT_TIME_JITTER 60
  49. #define DEFAULT_MAX_SIGS 5
  50. static const char *M = "rspamd dkim plugin";
  51. static const char default_sign_headers[] = ""
  52. "(o)from:(x)sender:(o)reply-to:(o)subject:(x)date:(x)message-id:"
  53. "(o)to:(o)cc:(x)mime-version:(x)content-type:(x)content-transfer-encoding:"
  54. "resent-to:resent-cc:resent-from:resent-sender:resent-message-id:"
  55. "(x)in-reply-to:(x)references:list-id:list-help:list-owner:list-unsubscribe:"
  56. "list-unsubscribe-post:list-subscribe:list-post:(x)openpgp:(x)autocrypt";
  57. static const char default_arc_sign_headers[] = ""
  58. "(o)from:(x)sender:(o)reply-to:(o)subject:(x)date:(x)message-id:"
  59. "(o)to:(o)cc:(x)mime-version:(x)content-type:(x)content-transfer-encoding:"
  60. "resent-to:resent-cc:resent-from:resent-sender:resent-message-id:"
  61. "(x)in-reply-to:(x)references:list-id:list-help:list-owner:list-unsubscribe:"
  62. "list-unsubscribe-post:list-subscribe:list-post:dkim-signature:(x)openpgp:"
  63. "(x)autocrypt";
  64. struct dkim_ctx {
  65. struct module_ctx ctx;
  66. const char *symbol_reject;
  67. const char *symbol_tempfail;
  68. const char *symbol_allow;
  69. const char *symbol_na;
  70. const char *symbol_permfail;
  71. struct rspamd_radix_map_helper *whitelist_ip;
  72. struct rspamd_hash_map_helper *dkim_domains;
  73. unsigned int strict_multiplier;
  74. unsigned int time_jitter;
  75. rspamd_lru_hash_t *dkim_hash;
  76. rspamd_lru_hash_t *dkim_sign_hash;
  77. const char *sign_headers;
  78. const char *arc_sign_headers;
  79. unsigned int max_sigs;
  80. gboolean trusted_only;
  81. gboolean check_local;
  82. gboolean check_authed;
  83. };
  84. struct dkim_check_result {
  85. rspamd_dkim_context_t *ctx;
  86. rspamd_dkim_key_t *key;
  87. struct rspamd_task *task;
  88. struct rspamd_dkim_check_result *res;
  89. double mult_allow;
  90. double mult_deny;
  91. struct rspamd_symcache_dynamic_item *item;
  92. struct dkim_check_result *next, *prev, *first;
  93. };
  94. static void dkim_symbol_callback(struct rspamd_task *task,
  95. struct rspamd_symcache_dynamic_item *item,
  96. void *unused);
  97. static int lua_dkim_sign_handler(lua_State *L);
  98. static int lua_dkim_verify_handler(lua_State *L);
  99. static int lua_dkim_canonicalize_handler(lua_State *L);
  100. /* Initialization */
  101. int dkim_module_init(struct rspamd_config *cfg, struct module_ctx **ctx);
  102. int dkim_module_config(struct rspamd_config *cfg, bool validate);
  103. int dkim_module_reconfig(struct rspamd_config *cfg);
  104. module_t dkim_module = {
  105. "dkim",
  106. dkim_module_init,
  107. dkim_module_config,
  108. dkim_module_reconfig,
  109. NULL,
  110. RSPAMD_MODULE_VER,
  111. (unsigned int) -1,
  112. };
  113. static inline struct dkim_ctx *
  114. dkim_get_context(struct rspamd_config *cfg)
  115. {
  116. return (struct dkim_ctx *) g_ptr_array_index(cfg->c_modules,
  117. dkim_module.ctx_offset);
  118. }
  119. static void
  120. dkim_module_key_dtor(gpointer k)
  121. {
  122. rspamd_dkim_key_t *key = k;
  123. rspamd_dkim_key_unref(key);
  124. }
  125. static void
  126. dkim_module_free_list(gpointer k)
  127. {
  128. g_list_free_full((GList *) k, rspamd_gstring_free_hard);
  129. }
  130. int dkim_module_init(struct rspamd_config *cfg, struct module_ctx **ctx)
  131. {
  132. struct dkim_ctx *dkim_module_ctx;
  133. dkim_module_ctx = rspamd_mempool_alloc0(cfg->cfg_pool,
  134. sizeof(*dkim_module_ctx));
  135. dkim_module_ctx->sign_headers = default_sign_headers;
  136. dkim_module_ctx->arc_sign_headers = default_arc_sign_headers;
  137. dkim_module_ctx->max_sigs = DEFAULT_MAX_SIGS;
  138. *ctx = (struct module_ctx *) dkim_module_ctx;
  139. rspamd_rcl_add_doc_by_path(cfg,
  140. NULL,
  141. "DKIM check plugin",
  142. "dkim",
  143. UCL_OBJECT,
  144. NULL,
  145. 0,
  146. NULL,
  147. 0);
  148. rspamd_rcl_add_doc_by_path(cfg,
  149. "dkim",
  150. "Map of IP addresses that should be excluded from DKIM checks",
  151. "whitelist",
  152. UCL_STRING,
  153. NULL,
  154. 0,
  155. NULL,
  156. 0);
  157. rspamd_rcl_add_doc_by_path(cfg,
  158. "dkim",
  159. "Symbol that is added if DKIM check is successful",
  160. "symbol_allow",
  161. UCL_STRING,
  162. NULL,
  163. 0,
  164. DEFAULT_SYMBOL_ALLOW,
  165. 0);
  166. rspamd_rcl_add_doc_by_path(cfg,
  167. "dkim",
  168. "Symbol that is added if DKIM check is unsuccessful",
  169. "symbol_reject",
  170. UCL_STRING,
  171. NULL,
  172. 0,
  173. DEFAULT_SYMBOL_REJECT,
  174. 0);
  175. rspamd_rcl_add_doc_by_path(cfg,
  176. "dkim",
  177. "Symbol that is added if DKIM check can't be completed (e.g. DNS failure)",
  178. "symbol_tempfail",
  179. UCL_STRING,
  180. NULL,
  181. 0,
  182. DEFAULT_SYMBOL_TEMPFAIL,
  183. 0);
  184. rspamd_rcl_add_doc_by_path(cfg,
  185. "dkim",
  186. "Symbol that is added if mail is not signed",
  187. "symbol_na",
  188. UCL_STRING,
  189. NULL,
  190. 0,
  191. DEFAULT_SYMBOL_NA,
  192. 0);
  193. rspamd_rcl_add_doc_by_path(cfg,
  194. "dkim",
  195. "Symbol that is added if permanent failure encountered",
  196. "symbol_permfail",
  197. UCL_STRING,
  198. NULL,
  199. 0,
  200. DEFAULT_SYMBOL_PERMFAIL,
  201. 0);
  202. rspamd_rcl_add_doc_by_path(cfg,
  203. "dkim",
  204. "Size of DKIM keys cache",
  205. "dkim_cache_size",
  206. UCL_INT,
  207. NULL,
  208. 0,
  209. G_STRINGIFY(DEFAULT_CACHE_SIZE),
  210. 0);
  211. rspamd_rcl_add_doc_by_path(cfg,
  212. "dkim",
  213. "Allow this time difference when checking DKIM signature time validity",
  214. "time_jitter",
  215. UCL_TIME,
  216. NULL,
  217. 0,
  218. G_STRINGIFY(DEFAULT_TIME_JITTER),
  219. 0);
  220. rspamd_rcl_add_doc_by_path(cfg,
  221. "dkim",
  222. "Domains to check DKIM for (check all domains if this option is empty)",
  223. "domains",
  224. UCL_STRING,
  225. NULL,
  226. 0,
  227. "empty",
  228. 0);
  229. rspamd_rcl_add_doc_by_path(cfg,
  230. "dkim",
  231. "Map of domains that are treated as 'trusted' meaning that DKIM policy failure has more significant score",
  232. "trusted_domains",
  233. UCL_STRING,
  234. NULL,
  235. 0,
  236. "empty",
  237. 0);
  238. rspamd_rcl_add_doc_by_path(cfg,
  239. "dkim",
  240. "Multiply dkim score by this factor for trusted domains",
  241. "strict_multiplier",
  242. UCL_FLOAT,
  243. NULL,
  244. 0,
  245. NULL,
  246. 0);
  247. rspamd_rcl_add_doc_by_path(cfg,
  248. "dkim",
  249. "Check DKIM policies merely for `trusted_domains`",
  250. "trusted_only",
  251. UCL_BOOLEAN,
  252. NULL,
  253. 0,
  254. "false",
  255. 0);
  256. rspamd_rcl_add_doc_by_path(cfg,
  257. "dkim",
  258. "Lua script that tells if a message should be signed and with what params (obsoleted)",
  259. "sign_condition",
  260. UCL_STRING,
  261. NULL,
  262. 0,
  263. "empty",
  264. 0);
  265. rspamd_rcl_add_doc_by_path(cfg,
  266. "dkim",
  267. "Obsoleted: maximum number of DKIM signatures to check",
  268. "max_sigs",
  269. UCL_INT,
  270. NULL,
  271. 0,
  272. "n/a",
  273. 0);
  274. rspamd_rcl_add_doc_by_path(cfg,
  275. "dkim",
  276. "Headers used in signing",
  277. "sign_headers",
  278. UCL_STRING,
  279. NULL,
  280. 0,
  281. default_sign_headers,
  282. 0);
  283. return 0;
  284. }
  285. int dkim_module_config(struct rspamd_config *cfg, bool validate)
  286. {
  287. const ucl_object_t *value;
  288. int res = TRUE, cb_id = -1;
  289. unsigned int cache_size, sign_cache_size;
  290. gboolean got_trusted = FALSE;
  291. struct dkim_ctx *dkim_module_ctx = dkim_get_context(cfg);
  292. /* Register global methods */
  293. lua_getglobal(cfg->lua_state, "rspamd_plugins");
  294. if (lua_type(cfg->lua_state, -1) == LUA_TTABLE) {
  295. lua_pushstring(cfg->lua_state, "dkim");
  296. lua_createtable(cfg->lua_state, 0, 1);
  297. /* Set methods */
  298. lua_pushstring(cfg->lua_state, "sign");
  299. lua_pushcfunction(cfg->lua_state, lua_dkim_sign_handler);
  300. lua_settable(cfg->lua_state, -3);
  301. lua_pushstring(cfg->lua_state, "verify");
  302. lua_pushcfunction(cfg->lua_state, lua_dkim_verify_handler);
  303. lua_settable(cfg->lua_state, -3);
  304. lua_pushstring(cfg->lua_state, "canon_header_relaxed");
  305. lua_pushcfunction(cfg->lua_state, lua_dkim_canonicalize_handler);
  306. lua_settable(cfg->lua_state, -3);
  307. /* Finish dkim key */
  308. lua_settable(cfg->lua_state, -3);
  309. }
  310. lua_pop(cfg->lua_state, 1); /* Remove global function */
  311. dkim_module_ctx->whitelist_ip = NULL;
  312. value = rspamd_config_get_module_opt(cfg, "dkim", "check_local");
  313. if (value == NULL) {
  314. value = rspamd_config_get_module_opt(cfg, "options", "check_local");
  315. }
  316. if (value != NULL) {
  317. dkim_module_ctx->check_local = ucl_object_toboolean(value);
  318. }
  319. else {
  320. dkim_module_ctx->check_local = FALSE;
  321. }
  322. value = rspamd_config_get_module_opt(cfg, "dkim",
  323. "check_authed");
  324. if (value == NULL) {
  325. value = rspamd_config_get_module_opt(cfg, "options",
  326. "check_authed");
  327. }
  328. if (value != NULL) {
  329. dkim_module_ctx->check_authed = ucl_object_toboolean(value);
  330. }
  331. else {
  332. dkim_module_ctx->check_authed = FALSE;
  333. }
  334. if ((value =
  335. rspamd_config_get_module_opt(cfg, "dkim", "symbol_reject")) != NULL) {
  336. dkim_module_ctx->symbol_reject = ucl_object_tostring(value);
  337. }
  338. else {
  339. dkim_module_ctx->symbol_reject = DEFAULT_SYMBOL_REJECT;
  340. }
  341. if ((value =
  342. rspamd_config_get_module_opt(cfg, "dkim",
  343. "symbol_tempfail")) != NULL) {
  344. dkim_module_ctx->symbol_tempfail = ucl_object_tostring(value);
  345. }
  346. else {
  347. dkim_module_ctx->symbol_tempfail = DEFAULT_SYMBOL_TEMPFAIL;
  348. }
  349. if ((value =
  350. rspamd_config_get_module_opt(cfg, "dkim", "symbol_allow")) != NULL) {
  351. dkim_module_ctx->symbol_allow = ucl_object_tostring(value);
  352. }
  353. else {
  354. dkim_module_ctx->symbol_allow = DEFAULT_SYMBOL_ALLOW;
  355. }
  356. if ((value =
  357. rspamd_config_get_module_opt(cfg, "dkim", "symbol_na")) != NULL) {
  358. dkim_module_ctx->symbol_na = ucl_object_tostring(value);
  359. }
  360. else {
  361. dkim_module_ctx->symbol_na = DEFAULT_SYMBOL_NA;
  362. }
  363. if ((value =
  364. rspamd_config_get_module_opt(cfg, "dkim", "symbol_permfail")) != NULL) {
  365. dkim_module_ctx->symbol_permfail = ucl_object_tostring(value);
  366. }
  367. else {
  368. dkim_module_ctx->symbol_permfail = DEFAULT_SYMBOL_PERMFAIL;
  369. }
  370. if ((value =
  371. rspamd_config_get_module_opt(cfg, "dkim",
  372. "dkim_cache_size")) != NULL) {
  373. cache_size = ucl_object_toint(value);
  374. }
  375. else {
  376. cache_size = DEFAULT_CACHE_SIZE;
  377. }
  378. if ((value =
  379. rspamd_config_get_module_opt(cfg, "dkim",
  380. "sign_cache_size")) != NULL) {
  381. sign_cache_size = ucl_object_toint(value);
  382. }
  383. else {
  384. sign_cache_size = 128;
  385. }
  386. if ((value =
  387. rspamd_config_get_module_opt(cfg, "dkim", "time_jitter")) != NULL) {
  388. dkim_module_ctx->time_jitter = ucl_object_todouble(value);
  389. }
  390. else {
  391. dkim_module_ctx->time_jitter = DEFAULT_TIME_JITTER;
  392. }
  393. if ((value =
  394. rspamd_config_get_module_opt(cfg, "dkim", "max_sigs")) != NULL) {
  395. dkim_module_ctx->max_sigs = ucl_object_toint(value);
  396. }
  397. if ((value =
  398. rspamd_config_get_module_opt(cfg, "dkim", "whitelist")) != NULL) {
  399. rspamd_config_radix_from_ucl(cfg, value, "DKIM whitelist",
  400. &dkim_module_ctx->whitelist_ip, NULL, NULL, "dkim whitelist");
  401. }
  402. if ((value =
  403. rspamd_config_get_module_opt(cfg, "dkim", "domains")) != NULL) {
  404. if (!rspamd_map_add_from_ucl(cfg, value,
  405. "DKIM domains",
  406. rspamd_kv_list_read,
  407. rspamd_kv_list_fin,
  408. rspamd_kv_list_dtor,
  409. (void **) &dkim_module_ctx->dkim_domains,
  410. NULL, RSPAMD_MAP_DEFAULT)) {
  411. msg_warn_config("cannot load dkim domains list from %s",
  412. ucl_object_tostring(value));
  413. }
  414. else {
  415. got_trusted = TRUE;
  416. }
  417. }
  418. if (!got_trusted && (value =
  419. rspamd_config_get_module_opt(cfg, "dkim", "trusted_domains")) != NULL) {
  420. if (!rspamd_map_add_from_ucl(cfg, value,
  421. "DKIM domains",
  422. rspamd_kv_list_read,
  423. rspamd_kv_list_fin,
  424. rspamd_kv_list_dtor,
  425. (void **) &dkim_module_ctx->dkim_domains,
  426. NULL, RSPAMD_MAP_DEFAULT)) {
  427. msg_warn_config("cannot load dkim domains list from %s",
  428. ucl_object_tostring(value));
  429. if (validate) {
  430. return FALSE;
  431. }
  432. }
  433. else {
  434. got_trusted = TRUE;
  435. }
  436. }
  437. if ((value =
  438. rspamd_config_get_module_opt(cfg, "dkim",
  439. "strict_multiplier")) != NULL) {
  440. dkim_module_ctx->strict_multiplier = ucl_object_toint(value);
  441. }
  442. else {
  443. dkim_module_ctx->strict_multiplier = 1;
  444. }
  445. if ((value =
  446. rspamd_config_get_module_opt(cfg, "dkim", "trusted_only")) != NULL) {
  447. dkim_module_ctx->trusted_only = ucl_object_toboolean(value);
  448. }
  449. else {
  450. dkim_module_ctx->trusted_only = FALSE;
  451. }
  452. if ((value =
  453. rspamd_config_get_module_opt(cfg, "dkim", "sign_headers")) != NULL) {
  454. dkim_module_ctx->sign_headers = ucl_object_tostring(value);
  455. }
  456. if ((value =
  457. rspamd_config_get_module_opt(cfg, "arc", "sign_headers")) != NULL) {
  458. dkim_module_ctx->arc_sign_headers = ucl_object_tostring(value);
  459. }
  460. if (cache_size > 0) {
  461. dkim_module_ctx->dkim_hash = rspamd_lru_hash_new(
  462. cache_size,
  463. g_free,
  464. dkim_module_key_dtor);
  465. rspamd_mempool_add_destructor(cfg->cfg_pool,
  466. (rspamd_mempool_destruct_t) rspamd_lru_hash_destroy,
  467. dkim_module_ctx->dkim_hash);
  468. }
  469. if (sign_cache_size > 0) {
  470. dkim_module_ctx->dkim_sign_hash = rspamd_lru_hash_new(
  471. sign_cache_size,
  472. g_free,
  473. (GDestroyNotify) rspamd_dkim_sign_key_unref);
  474. rspamd_mempool_add_destructor(cfg->cfg_pool,
  475. (rspamd_mempool_destruct_t) rspamd_lru_hash_destroy,
  476. dkim_module_ctx->dkim_sign_hash);
  477. }
  478. if (dkim_module_ctx->trusted_only && !got_trusted) {
  479. msg_err_config("trusted_only option is set and no trusted domains are defined");
  480. if (validate) {
  481. return FALSE;
  482. }
  483. }
  484. else {
  485. if (!rspamd_config_is_module_enabled(cfg, "dkim")) {
  486. return TRUE;
  487. }
  488. cb_id = rspamd_symcache_add_symbol(cfg->cache,
  489. "DKIM_CHECK",
  490. 0,
  491. dkim_symbol_callback,
  492. NULL,
  493. SYMBOL_TYPE_CALLBACK,
  494. -1);
  495. rspamd_config_add_symbol(cfg,
  496. "DKIM_CHECK",
  497. 0.0,
  498. "DKIM check callback",
  499. "policies",
  500. RSPAMD_SYMBOL_FLAG_IGNORE_METRIC,
  501. 1,
  502. 1);
  503. rspamd_config_add_symbol_group(cfg, "DKIM_CHECK", "dkim");
  504. rspamd_symcache_add_symbol(cfg->cache,
  505. dkim_module_ctx->symbol_reject,
  506. 0,
  507. NULL,
  508. NULL,
  509. SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
  510. cb_id);
  511. rspamd_symcache_add_symbol(cfg->cache,
  512. dkim_module_ctx->symbol_na,
  513. 0,
  514. NULL, NULL,
  515. SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
  516. cb_id);
  517. rspamd_symcache_add_symbol(cfg->cache,
  518. dkim_module_ctx->symbol_permfail,
  519. 0,
  520. NULL, NULL,
  521. SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
  522. cb_id);
  523. rspamd_symcache_add_symbol(cfg->cache,
  524. dkim_module_ctx->symbol_tempfail,
  525. 0,
  526. NULL, NULL,
  527. SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
  528. cb_id);
  529. rspamd_symcache_add_symbol(cfg->cache,
  530. dkim_module_ctx->symbol_allow,
  531. 0,
  532. NULL, NULL,
  533. SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
  534. cb_id);
  535. rspamd_symcache_add_symbol(cfg->cache,
  536. "DKIM_TRACE",
  537. 0,
  538. NULL, NULL,
  539. SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_NOSTAT,
  540. cb_id);
  541. rspamd_config_add_symbol(cfg,
  542. "DKIM_TRACE",
  543. 0.0,
  544. "DKIM trace symbol",
  545. "policies",
  546. RSPAMD_SYMBOL_FLAG_IGNORE_METRIC,
  547. 1,
  548. 1);
  549. rspamd_config_add_symbol_group(cfg, "DKIM_TRACE", "dkim");
  550. msg_info_config("init internal dkim module");
  551. #ifndef HAVE_OPENSSL
  552. msg_warn_config(
  553. "openssl is not found so dkim rsa check is disabled, only check body hash, it is NOT safe to trust these results");
  554. #endif
  555. }
  556. return res;
  557. }
  558. /**
  559. * Grab a private key from the cache
  560. * or from the key content provided
  561. */
  562. rspamd_dkim_sign_key_t *
  563. dkim_module_load_key_format(struct rspamd_task *task,
  564. struct dkim_ctx *dkim_module_ctx,
  565. const char *key, gsize keylen,
  566. enum rspamd_dkim_key_format key_format)
  567. {
  568. unsigned char h[rspamd_cryptobox_HASHBYTES],
  569. hex_hash[rspamd_cryptobox_HASHBYTES * 2 + 1];
  570. rspamd_dkim_sign_key_t *ret = NULL;
  571. GError *err = NULL;
  572. struct stat st;
  573. memset(hex_hash, 0, sizeof(hex_hash));
  574. rspamd_cryptobox_hash(h, key, keylen, NULL, 0);
  575. rspamd_encode_hex_buf(h, sizeof(h), hex_hash, sizeof(hex_hash));
  576. if (dkim_module_ctx->dkim_sign_hash) {
  577. ret = rspamd_lru_hash_lookup(dkim_module_ctx->dkim_sign_hash,
  578. hex_hash, time(NULL));
  579. }
  580. /*
  581. * This fails for paths that are also valid base64.
  582. * Maybe the caller should have specified a format.
  583. */
  584. if (key_format == RSPAMD_DKIM_KEY_UNKNOWN) {
  585. if (key[0] == '.' || key[0] == '/') {
  586. if (!rspamd_cryptobox_base64_is_valid(key, keylen)) {
  587. key_format = RSPAMD_DKIM_KEY_FILE;
  588. }
  589. }
  590. else if (rspamd_cryptobox_base64_is_valid(key, keylen)) {
  591. key_format = RSPAMD_DKIM_KEY_BASE64;
  592. }
  593. }
  594. if (ret != NULL && key_format == RSPAMD_DKIM_KEY_FILE) {
  595. msg_debug_task("checking for stale file key");
  596. if (stat(key, &st) != 0) {
  597. msg_err_task("cannot stat key file: %s", strerror(errno));
  598. return NULL;
  599. }
  600. if (rspamd_dkim_sign_key_maybe_invalidate(ret, st.st_mtime)) {
  601. msg_debug_task("removing stale file key");
  602. /*
  603. * Invalidate DKIM key
  604. * removal from lru cache also cleanup the key and value
  605. */
  606. if (dkim_module_ctx->dkim_sign_hash) {
  607. rspamd_lru_hash_remove(dkim_module_ctx->dkim_sign_hash,
  608. hex_hash);
  609. }
  610. ret = NULL;
  611. }
  612. }
  613. /* found key; done */
  614. if (ret != NULL) {
  615. return ret;
  616. }
  617. ret = rspamd_dkim_sign_key_load(key, keylen, key_format, &err);
  618. if (ret == NULL) {
  619. msg_err_task("cannot load dkim key %s: %e",
  620. key, err);
  621. g_error_free(err);
  622. }
  623. else if (dkim_module_ctx->dkim_sign_hash) {
  624. rspamd_lru_hash_insert(dkim_module_ctx->dkim_sign_hash,
  625. g_strdup(hex_hash), ret, time(NULL), 0);
  626. }
  627. return ret;
  628. }
  629. static int
  630. lua_dkim_sign_handler(lua_State *L)
  631. {
  632. struct rspamd_task *task = lua_check_task(L, 1);
  633. int64_t arc_idx = 0, expire = 0;
  634. enum rspamd_dkim_type sign_type = RSPAMD_DKIM_NORMAL;
  635. GError *err = NULL;
  636. GString *hdr;
  637. GList *sigs = NULL;
  638. const char *selector = NULL, *domain = NULL, *key = NULL, *rawkey = NULL,
  639. *headers = NULL, *sign_type_str = NULL, *arc_cv = NULL,
  640. *pubkey = NULL;
  641. rspamd_dkim_sign_context_t *ctx;
  642. rspamd_dkim_sign_key_t *dkim_key;
  643. gsize rawlen = 0, keylen = 0;
  644. gboolean no_cache = FALSE, strict_pubkey_check = FALSE;
  645. struct dkim_ctx *dkim_module_ctx;
  646. luaL_argcheck(L, lua_type(L, 2) == LUA_TTABLE, 2, "'table' expected");
  647. /*
  648. * Get the following elements:
  649. * - selector
  650. * - domain
  651. * - key
  652. */
  653. if (!rspamd_lua_parse_table_arguments(L, 2, &err,
  654. RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
  655. "key=V;rawkey=V;*domain=S;*selector=S;no_cache=B;headers=S;"
  656. "sign_type=S;arc_idx=I;arc_cv=S;expire=I;pubkey=S;"
  657. "strict_pubkey_check=B",
  658. &keylen, &key, &rawlen, &rawkey, &domain,
  659. &selector, &no_cache, &headers,
  660. &sign_type_str, &arc_idx, &arc_cv, &expire, &pubkey,
  661. &strict_pubkey_check)) {
  662. msg_err_task("cannot parse table arguments: %e",
  663. err);
  664. g_error_free(err);
  665. lua_pushboolean(L, FALSE);
  666. return 1;
  667. }
  668. dkim_module_ctx = dkim_get_context(task->cfg);
  669. if (key) {
  670. dkim_key = dkim_module_load_key_format(task, dkim_module_ctx, key,
  671. keylen, RSPAMD_DKIM_KEY_UNKNOWN);
  672. }
  673. else if (rawkey) {
  674. dkim_key = dkim_module_load_key_format(task, dkim_module_ctx, rawkey,
  675. rawlen, RSPAMD_DKIM_KEY_UNKNOWN);
  676. }
  677. else {
  678. msg_err_task("neither key nor rawkey are specified");
  679. lua_pushboolean(L, FALSE);
  680. return 1;
  681. }
  682. if (dkim_key == NULL) {
  683. lua_pushboolean(L, FALSE);
  684. return 1;
  685. }
  686. if (sign_type_str) {
  687. if (strcmp(sign_type_str, "dkim") == 0) {
  688. sign_type = RSPAMD_DKIM_NORMAL;
  689. if (headers == NULL) {
  690. headers = dkim_module_ctx->sign_headers;
  691. }
  692. }
  693. else if (strcmp(sign_type_str, "arc-sign") == 0) {
  694. sign_type = RSPAMD_DKIM_ARC_SIG;
  695. if (headers == NULL) {
  696. headers = dkim_module_ctx->arc_sign_headers;
  697. }
  698. if (arc_idx == 0) {
  699. lua_settop(L, 0);
  700. return luaL_error(L, "no arc idx specified");
  701. }
  702. }
  703. else if (strcmp(sign_type_str, "arc-seal") == 0) {
  704. sign_type = RSPAMD_DKIM_ARC_SEAL;
  705. if (arc_cv == NULL) {
  706. lua_settop(L, 0);
  707. return luaL_error(L, "no arc cv specified");
  708. }
  709. if (arc_idx == 0) {
  710. lua_settop(L, 0);
  711. return luaL_error(L, "no arc idx specified");
  712. }
  713. }
  714. else {
  715. lua_settop(L, 0);
  716. return luaL_error(L, "unknown sign type: %s",
  717. sign_type_str);
  718. }
  719. }
  720. else {
  721. /* Unspecified sign type, assume plain dkim */
  722. if (headers == NULL) {
  723. headers = dkim_module_ctx->sign_headers;
  724. }
  725. }
  726. if (pubkey != NULL) {
  727. /* Also check if private and public keys match */
  728. rspamd_dkim_key_t *pk;
  729. keylen = strlen(pubkey);
  730. pk = rspamd_dkim_parse_key(pubkey, &keylen, NULL);
  731. if (pk == NULL) {
  732. if (strict_pubkey_check) {
  733. msg_err_task("cannot parse pubkey from string: %s, skip signing",
  734. pubkey);
  735. lua_pushboolean(L, FALSE);
  736. return 1;
  737. }
  738. else {
  739. msg_warn_task("cannot parse pubkey from string: %s",
  740. pubkey);
  741. }
  742. }
  743. else {
  744. GError *te = NULL;
  745. /* We have parsed the key, so try to check keys */
  746. if (!rspamd_dkim_match_keys(pk, dkim_key, &te)) {
  747. if (strict_pubkey_check) {
  748. msg_err_task("public key for %s/%s does not match private "
  749. "key: %e, skip signing",
  750. domain, selector, te);
  751. g_error_free(te);
  752. lua_pushboolean(L, FALSE);
  753. rspamd_dkim_key_unref(pk);
  754. return 1;
  755. }
  756. else {
  757. msg_warn_task("public key for %s/%s does not match private "
  758. "key: %e",
  759. domain, selector, te);
  760. g_error_free(te);
  761. }
  762. }
  763. rspamd_dkim_key_unref(pk);
  764. }
  765. }
  766. ctx = rspamd_create_dkim_sign_context(task, dkim_key,
  767. DKIM_CANON_RELAXED, DKIM_CANON_RELAXED,
  768. headers, sign_type, &err);
  769. if (ctx == NULL) {
  770. msg_err_task("cannot create sign context: %e",
  771. err);
  772. g_error_free(err);
  773. lua_pushboolean(L, FALSE);
  774. return 1;
  775. }
  776. hdr = rspamd_dkim_sign(task, selector, domain, 0,
  777. expire, arc_idx, arc_cv, ctx);
  778. if (hdr) {
  779. if (!no_cache) {
  780. sigs = rspamd_mempool_get_variable(task->task_pool, "dkim-signature");
  781. if (sigs == NULL) {
  782. sigs = g_list_append(sigs, hdr);
  783. rspamd_mempool_set_variable(task->task_pool, "dkim-signature",
  784. sigs, dkim_module_free_list);
  785. }
  786. else {
  787. sigs = g_list_append(sigs, hdr);
  788. (void) sigs;
  789. }
  790. }
  791. lua_pushboolean(L, TRUE);
  792. lua_pushlstring(L, hdr->str, hdr->len);
  793. if (no_cache) {
  794. g_string_free(hdr, TRUE);
  795. }
  796. return 2;
  797. }
  798. lua_pushboolean(L, FALSE);
  799. lua_pushnil(L);
  800. return 2;
  801. }
  802. int dkim_module_reconfig(struct rspamd_config *cfg)
  803. {
  804. return dkim_module_config(cfg, false);
  805. }
  806. /*
  807. * Parse strict value for domain in format: 'reject_multiplier:deny_multiplier'
  808. */
  809. static gboolean
  810. dkim_module_parse_strict(const char *value, double *allow, double *deny)
  811. {
  812. const char *colon;
  813. char *err = NULL;
  814. double val;
  815. char numbuf[64];
  816. colon = strchr(value, ':');
  817. if (colon) {
  818. rspamd_strlcpy(numbuf, value,
  819. MIN(sizeof(numbuf), (colon - value) + 1));
  820. val = strtod(numbuf, &err);
  821. if (err == NULL || *err == '\0') {
  822. *deny = val;
  823. colon++;
  824. rspamd_strlcpy(numbuf, colon, sizeof(numbuf));
  825. err = NULL;
  826. val = strtod(numbuf, &err);
  827. if (err == NULL || *err == '\0') {
  828. *allow = val;
  829. return TRUE;
  830. }
  831. }
  832. }
  833. return FALSE;
  834. }
  835. static void
  836. dkim_module_check(struct dkim_check_result *res)
  837. {
  838. gboolean all_done = TRUE;
  839. const char *strict_value;
  840. struct dkim_check_result *first, *cur = NULL;
  841. struct dkim_ctx *dkim_module_ctx = dkim_get_context(res->task->cfg);
  842. struct rspamd_task *task = res->task;
  843. first = res->first;
  844. DL_FOREACH(first, cur)
  845. {
  846. if (cur->ctx == NULL) {
  847. continue;
  848. }
  849. if (cur->key != NULL && cur->res == NULL) {
  850. cur->res = rspamd_dkim_check(cur->ctx, cur->key, task);
  851. if (dkim_module_ctx->dkim_domains != NULL) {
  852. /* Perform strict check */
  853. const char *domain = rspamd_dkim_get_domain(cur->ctx);
  854. if ((strict_value =
  855. rspamd_match_hash_map(dkim_module_ctx->dkim_domains,
  856. domain,
  857. strlen(domain))) != NULL) {
  858. if (!dkim_module_parse_strict(strict_value, &cur->mult_allow,
  859. &cur->mult_deny)) {
  860. cur->mult_allow = dkim_module_ctx->strict_multiplier;
  861. cur->mult_deny = dkim_module_ctx->strict_multiplier;
  862. }
  863. }
  864. }
  865. }
  866. }
  867. DL_FOREACH(first, cur)
  868. {
  869. if (cur->ctx == NULL) {
  870. continue;
  871. }
  872. if (cur->res == NULL) {
  873. /* Still need a key */
  874. all_done = FALSE;
  875. }
  876. }
  877. if (all_done) {
  878. /* Create zero terminated array of results */
  879. struct rspamd_dkim_check_result **pres;
  880. unsigned int nres = 0, i = 0;
  881. DL_FOREACH(first, cur)
  882. {
  883. if (cur->ctx == NULL || cur->res == NULL) {
  884. continue;
  885. }
  886. nres++;
  887. }
  888. pres = rspamd_mempool_alloc(task->task_pool, sizeof(*pres) * (nres + 1));
  889. pres[nres] = NULL;
  890. DL_FOREACH(first, cur)
  891. {
  892. const char *symbol = NULL, *trace = NULL;
  893. double symbol_weight = 1.0;
  894. if (cur->ctx == NULL || cur->res == NULL) {
  895. continue;
  896. }
  897. pres[i++] = cur->res;
  898. if (cur->res->rcode == DKIM_REJECT) {
  899. symbol = dkim_module_ctx->symbol_reject;
  900. trace = "-";
  901. symbol_weight = cur->mult_deny * 1.0;
  902. }
  903. else if (cur->res->rcode == DKIM_CONTINUE) {
  904. symbol = dkim_module_ctx->symbol_allow;
  905. trace = "+";
  906. symbol_weight = cur->mult_allow * 1.0;
  907. }
  908. else if (cur->res->rcode == DKIM_PERM_ERROR) {
  909. trace = "~";
  910. symbol = dkim_module_ctx->symbol_permfail;
  911. }
  912. else if (cur->res->rcode == DKIM_TRYAGAIN) {
  913. trace = "?";
  914. symbol = dkim_module_ctx->symbol_tempfail;
  915. }
  916. if (symbol != NULL) {
  917. const char *domain = rspamd_dkim_get_domain(cur->ctx);
  918. const char *selector = rspamd_dkim_get_selector(cur->ctx);
  919. gsize tracelen;
  920. char *tracebuf;
  921. tracelen = strlen(domain) + strlen(selector) + 4;
  922. tracebuf = rspamd_mempool_alloc(task->task_pool,
  923. tracelen);
  924. rspamd_snprintf(tracebuf, tracelen, "%s:%s", domain, trace);
  925. rspamd_task_insert_result(cur->task,
  926. "DKIM_TRACE",
  927. 0.0,
  928. tracebuf);
  929. rspamd_snprintf(tracebuf, tracelen, "%s:s=%s", domain, selector);
  930. rspamd_task_insert_result(task,
  931. symbol,
  932. symbol_weight,
  933. tracebuf);
  934. }
  935. }
  936. rspamd_mempool_set_variable(task->task_pool,
  937. RSPAMD_MEMPOOL_DKIM_CHECK_RESULTS,
  938. pres, NULL);
  939. }
  940. }
  941. static void
  942. dkim_module_key_handler(rspamd_dkim_key_t *key,
  943. gsize keylen,
  944. rspamd_dkim_context_t *ctx,
  945. gpointer ud,
  946. GError *err)
  947. {
  948. struct dkim_check_result *res = ud;
  949. struct rspamd_task *task;
  950. struct dkim_ctx *dkim_module_ctx;
  951. task = res->task;
  952. dkim_module_ctx = dkim_get_context(task->cfg);
  953. if (key != NULL) {
  954. /* Another ref belongs to the check context */
  955. res->key = rspamd_dkim_key_ref(key);
  956. /*
  957. * We actually receive key with refcount = 1, so we just assume that
  958. * lru hash owns this object now
  959. */
  960. /* Release key when task is processed */
  961. rspamd_mempool_add_destructor(res->task->task_pool,
  962. dkim_module_key_dtor, res->key);
  963. if (dkim_module_ctx->dkim_hash) {
  964. rspamd_lru_hash_insert(dkim_module_ctx->dkim_hash,
  965. g_strdup(rspamd_dkim_get_dns_key(ctx)),
  966. key, res->task->task_timestamp, rspamd_dkim_key_get_ttl(key));
  967. msg_info_task("stored DKIM key for %s in LRU cache for %d seconds, "
  968. "%d/%d elements in the cache",
  969. rspamd_dkim_get_dns_key(ctx),
  970. rspamd_dkim_key_get_ttl(key),
  971. rspamd_lru_hash_size(dkim_module_ctx->dkim_hash),
  972. rspamd_lru_hash_capacity(dkim_module_ctx->dkim_hash));
  973. }
  974. }
  975. else {
  976. /* Insert tempfail symbol */
  977. msg_info_task("cannot get key for domain %s: %e",
  978. rspamd_dkim_get_dns_key(ctx), err);
  979. if (err != NULL) {
  980. if (err->code == DKIM_SIGERROR_NOKEY) {
  981. res->res = rspamd_dkim_create_result(ctx, DKIM_TRYAGAIN, task);
  982. res->res->fail_reason = "DNS error when getting key";
  983. }
  984. else {
  985. res->res = rspamd_dkim_create_result(ctx, DKIM_PERM_ERROR, task);
  986. res->res->fail_reason = "invalid DKIM record";
  987. }
  988. }
  989. }
  990. if (err) {
  991. g_error_free(err);
  992. }
  993. dkim_module_check(res);
  994. }
  995. static void
  996. dkim_symbol_callback(struct rspamd_task *task,
  997. struct rspamd_symcache_dynamic_item *item,
  998. void *unused)
  999. {
  1000. rspamd_dkim_context_t *ctx;
  1001. rspamd_dkim_key_t *key;
  1002. GError *err = NULL;
  1003. struct rspamd_mime_header *rh, *rh_cur;
  1004. struct dkim_check_result *res = NULL, *cur;
  1005. unsigned int checked = 0;
  1006. double *dmarc_checks;
  1007. struct dkim_ctx *dkim_module_ctx = dkim_get_context(task->cfg);
  1008. /* Allow dmarc */
  1009. dmarc_checks = rspamd_mempool_get_variable(task->task_pool,
  1010. RSPAMD_MEMPOOL_DMARC_CHECKS);
  1011. if (dmarc_checks) {
  1012. (*dmarc_checks)++;
  1013. }
  1014. else {
  1015. dmarc_checks = rspamd_mempool_alloc(task->task_pool,
  1016. sizeof(*dmarc_checks));
  1017. *dmarc_checks = 1;
  1018. rspamd_mempool_set_variable(task->task_pool,
  1019. RSPAMD_MEMPOOL_DMARC_CHECKS,
  1020. dmarc_checks, NULL);
  1021. }
  1022. /* First check if plugin should be enabled */
  1023. if ((!dkim_module_ctx->check_authed && task->auth_user != NULL) || (!dkim_module_ctx->check_local &&
  1024. rspamd_ip_is_local_cfg(task->cfg, task->from_addr))) {
  1025. msg_info_task("skip DKIM checks for local networks and authorized users");
  1026. rspamd_symcache_finalize_item(task, item);
  1027. return;
  1028. }
  1029. /* Check whitelist */
  1030. if (rspamd_match_radix_map_addr(dkim_module_ctx->whitelist_ip,
  1031. task->from_addr) != NULL) {
  1032. msg_info_task("skip DKIM checks for whitelisted address");
  1033. rspamd_symcache_finalize_item(task, item);
  1034. return;
  1035. }
  1036. rspamd_symcache_item_async_inc(task, item, M);
  1037. /* Now check if a message has its signature */
  1038. rh = rspamd_message_get_header_array(task, RSPAMD_DKIM_SIGNHEADER, FALSE);
  1039. if (rh) {
  1040. msg_debug_task("dkim signature found");
  1041. DL_FOREACH(rh, rh_cur)
  1042. {
  1043. if (rh_cur->decoded == NULL || rh_cur->decoded[0] == '\0') {
  1044. msg_info_task("cannot load empty DKIM signature");
  1045. continue;
  1046. }
  1047. cur = rspamd_mempool_alloc0(task->task_pool, sizeof(*cur));
  1048. cur->first = res;
  1049. cur->res = NULL;
  1050. cur->task = task;
  1051. cur->mult_allow = 1.0;
  1052. cur->mult_deny = 1.0;
  1053. cur->item = item;
  1054. ctx = rspamd_create_dkim_context(rh_cur->decoded,
  1055. task->task_pool,
  1056. task->resolver,
  1057. dkim_module_ctx->time_jitter,
  1058. RSPAMD_DKIM_NORMAL,
  1059. &err);
  1060. if (res == NULL) {
  1061. res = cur;
  1062. res->first = res;
  1063. res->prev = res;
  1064. }
  1065. else {
  1066. DL_APPEND(res, cur);
  1067. }
  1068. if (ctx == NULL) {
  1069. if (err != NULL) {
  1070. msg_info_task("cannot parse DKIM signature: %e",
  1071. err);
  1072. g_error_free(err);
  1073. err = NULL;
  1074. }
  1075. else {
  1076. msg_info_task("cannot parse DKIM signature: "
  1077. "unknown error");
  1078. }
  1079. continue;
  1080. }
  1081. else {
  1082. /* Get key */
  1083. cur->ctx = ctx;
  1084. const char *domain = rspamd_dkim_get_domain(cur->ctx);
  1085. if (dkim_module_ctx->trusted_only &&
  1086. (dkim_module_ctx->dkim_domains == NULL ||
  1087. rspamd_match_hash_map(dkim_module_ctx->dkim_domains,
  1088. domain, strlen(domain)) == NULL)) {
  1089. msg_debug_task("skip dkim check for %s domain",
  1090. rspamd_dkim_get_domain(ctx));
  1091. continue;
  1092. }
  1093. if (dkim_module_ctx->dkim_hash) {
  1094. key = rspamd_lru_hash_lookup(dkim_module_ctx->dkim_hash,
  1095. rspamd_dkim_get_dns_key(ctx),
  1096. task->task_timestamp);
  1097. }
  1098. else {
  1099. key = NULL;
  1100. }
  1101. if (key != NULL) {
  1102. cur->key = rspamd_dkim_key_ref(key);
  1103. /* Release key when task is processed */
  1104. rspamd_mempool_add_destructor(task->task_pool,
  1105. dkim_module_key_dtor, cur->key);
  1106. }
  1107. else {
  1108. if (!rspamd_get_dkim_key(ctx,
  1109. task,
  1110. dkim_module_key_handler,
  1111. cur)) {
  1112. continue;
  1113. }
  1114. }
  1115. }
  1116. checked++;
  1117. if (checked > dkim_module_ctx->max_sigs) {
  1118. msg_info_task("message has multiple signatures but we"
  1119. " stopped after %d checked signatures as limit"
  1120. " is reached",
  1121. checked);
  1122. break;
  1123. }
  1124. }
  1125. }
  1126. else {
  1127. rspamd_task_insert_result(task,
  1128. dkim_module_ctx->symbol_na,
  1129. 1.0,
  1130. NULL);
  1131. }
  1132. if (res != NULL) {
  1133. dkim_module_check(res);
  1134. }
  1135. rspamd_symcache_item_async_dec_check(task, item, M);
  1136. }
  1137. struct rspamd_dkim_lua_verify_cbdata {
  1138. rspamd_dkim_context_t *ctx;
  1139. struct rspamd_task *task;
  1140. lua_State *L;
  1141. rspamd_dkim_key_t *key;
  1142. int cbref;
  1143. };
  1144. static void
  1145. dkim_module_lua_push_verify_result(struct rspamd_dkim_lua_verify_cbdata *cbd,
  1146. struct rspamd_dkim_check_result *res, GError *err)
  1147. {
  1148. struct rspamd_task **ptask, *task;
  1149. const char *error_str = "unknown error";
  1150. gboolean success = FALSE;
  1151. task = cbd->task;
  1152. switch (res->rcode) {
  1153. case DKIM_CONTINUE:
  1154. error_str = NULL;
  1155. success = TRUE;
  1156. break;
  1157. case DKIM_REJECT:
  1158. if (err) {
  1159. error_str = err->message;
  1160. }
  1161. else {
  1162. error_str = "reject";
  1163. }
  1164. break;
  1165. case DKIM_TRYAGAIN:
  1166. if (err) {
  1167. error_str = err->message;
  1168. }
  1169. else {
  1170. error_str = "tempfail";
  1171. }
  1172. break;
  1173. case DKIM_NOTFOUND:
  1174. if (err) {
  1175. error_str = err->message;
  1176. }
  1177. else {
  1178. error_str = "not found";
  1179. }
  1180. break;
  1181. case DKIM_RECORD_ERROR:
  1182. if (err) {
  1183. error_str = err->message;
  1184. }
  1185. else {
  1186. error_str = "bad record";
  1187. }
  1188. break;
  1189. case DKIM_PERM_ERROR:
  1190. if (err) {
  1191. error_str = err->message;
  1192. }
  1193. else {
  1194. error_str = "permanent error";
  1195. }
  1196. break;
  1197. default:
  1198. break;
  1199. }
  1200. lua_rawgeti(cbd->L, LUA_REGISTRYINDEX, cbd->cbref);
  1201. ptask = lua_newuserdata(cbd->L, sizeof(*ptask));
  1202. *ptask = task;
  1203. lua_pushboolean(cbd->L, success);
  1204. if (error_str) {
  1205. lua_pushstring(cbd->L, error_str);
  1206. }
  1207. else {
  1208. lua_pushnil(cbd->L);
  1209. }
  1210. if (cbd->ctx) {
  1211. if (res->domain) {
  1212. lua_pushstring(cbd->L, res->domain);
  1213. }
  1214. else {
  1215. lua_pushnil(cbd->L);
  1216. }
  1217. if (res->selector) {
  1218. lua_pushstring(cbd->L, res->selector);
  1219. }
  1220. else {
  1221. lua_pushnil(cbd->L);
  1222. }
  1223. if (res->short_b) {
  1224. lua_pushstring(cbd->L, res->short_b);
  1225. }
  1226. else {
  1227. lua_pushnil(cbd->L);
  1228. }
  1229. if (res->fail_reason) {
  1230. lua_pushstring(cbd->L, res->fail_reason);
  1231. }
  1232. else {
  1233. lua_pushnil(cbd->L);
  1234. }
  1235. }
  1236. else {
  1237. lua_pushnil(cbd->L);
  1238. lua_pushnil(cbd->L);
  1239. lua_pushnil(cbd->L);
  1240. lua_pushnil(cbd->L);
  1241. }
  1242. if (lua_pcall(cbd->L, 7, 0, 0) != 0) {
  1243. msg_err_task("call to verify callback failed: %s",
  1244. lua_tostring(cbd->L, -1));
  1245. lua_pop(cbd->L, 1);
  1246. }
  1247. luaL_unref(cbd->L, LUA_REGISTRYINDEX, cbd->cbref);
  1248. }
  1249. static void
  1250. dkim_module_lua_on_key(rspamd_dkim_key_t *key,
  1251. gsize keylen,
  1252. rspamd_dkim_context_t *ctx,
  1253. gpointer ud,
  1254. GError *err)
  1255. {
  1256. struct rspamd_dkim_lua_verify_cbdata *cbd = ud;
  1257. struct rspamd_task *task;
  1258. struct rspamd_dkim_check_result *res;
  1259. struct dkim_ctx *dkim_module_ctx;
  1260. task = cbd->task;
  1261. dkim_module_ctx = dkim_get_context(task->cfg);
  1262. if (key != NULL) {
  1263. /* Another ref belongs to the check context */
  1264. cbd->key = rspamd_dkim_key_ref(key);
  1265. /*
  1266. * We actually receive key with refcount = 1, so we just assume that
  1267. * lru hash owns this object now
  1268. */
  1269. if (dkim_module_ctx->dkim_hash) {
  1270. rspamd_lru_hash_insert(dkim_module_ctx->dkim_hash,
  1271. g_strdup(rspamd_dkim_get_dns_key(ctx)),
  1272. key, cbd->task->task_timestamp, rspamd_dkim_key_get_ttl(key));
  1273. }
  1274. /* Release key when task is processed */
  1275. rspamd_mempool_add_destructor(cbd->task->task_pool,
  1276. dkim_module_key_dtor, cbd->key);
  1277. }
  1278. else {
  1279. /* Insert tempfail symbol */
  1280. msg_info_task("cannot get key for domain %s: %e",
  1281. rspamd_dkim_get_dns_key(ctx), err);
  1282. if (err != NULL) {
  1283. if (err->code == DKIM_SIGERROR_NOKEY) {
  1284. res = rspamd_dkim_create_result(ctx, DKIM_TRYAGAIN, task);
  1285. res->fail_reason = "DNS error when getting key";
  1286. }
  1287. else {
  1288. res = rspamd_dkim_create_result(ctx, DKIM_PERM_ERROR, task);
  1289. res->fail_reason = "invalid DKIM record";
  1290. }
  1291. }
  1292. else {
  1293. res = rspamd_dkim_create_result(ctx, DKIM_TRYAGAIN, task);
  1294. res->fail_reason = "DNS error when getting key";
  1295. }
  1296. dkim_module_lua_push_verify_result(cbd, res, err);
  1297. if (err) {
  1298. g_error_free(err);
  1299. }
  1300. return;
  1301. }
  1302. res = rspamd_dkim_check(cbd->ctx, cbd->key, cbd->task);
  1303. dkim_module_lua_push_verify_result(cbd, res, NULL);
  1304. }
  1305. static int
  1306. lua_dkim_verify_handler(lua_State *L)
  1307. {
  1308. struct rspamd_task *task = lua_check_task(L, 1);
  1309. const char *sig = luaL_checkstring(L, 2);
  1310. rspamd_dkim_context_t *ctx;
  1311. struct rspamd_dkim_lua_verify_cbdata *cbd;
  1312. rspamd_dkim_key_t *key;
  1313. struct rspamd_dkim_check_result *ret;
  1314. GError *err = NULL;
  1315. const char *type_str = NULL;
  1316. enum rspamd_dkim_type type = RSPAMD_DKIM_NORMAL;
  1317. struct dkim_ctx *dkim_module_ctx;
  1318. if (task && sig && lua_isfunction(L, 3)) {
  1319. if (lua_isstring(L, 4)) {
  1320. type_str = lua_tostring(L, 4);
  1321. if (type_str) {
  1322. if (strcmp(type_str, "dkim") == 0) {
  1323. type = RSPAMD_DKIM_NORMAL;
  1324. }
  1325. else if (strcmp(type_str, "arc-sign") == 0) {
  1326. type = RSPAMD_DKIM_ARC_SIG;
  1327. }
  1328. else if (strcmp(type_str, "arc-seal") == 0) {
  1329. type = RSPAMD_DKIM_ARC_SEAL;
  1330. }
  1331. else {
  1332. lua_settop(L, 0);
  1333. return luaL_error(L, "unknown sign type: %s",
  1334. type_str);
  1335. }
  1336. }
  1337. }
  1338. dkim_module_ctx = dkim_get_context(task->cfg);
  1339. ctx = rspamd_create_dkim_context(sig,
  1340. task->task_pool,
  1341. task->resolver,
  1342. dkim_module_ctx->time_jitter,
  1343. type,
  1344. &err);
  1345. if (ctx == NULL) {
  1346. lua_pushboolean(L, false);
  1347. if (err) {
  1348. lua_pushstring(L, err->message);
  1349. g_error_free(err);
  1350. }
  1351. else {
  1352. lua_pushstring(L, "unknown error");
  1353. }
  1354. return 2;
  1355. }
  1356. cbd = rspamd_mempool_alloc(task->task_pool, sizeof(*cbd));
  1357. cbd->L = L;
  1358. cbd->task = task;
  1359. lua_pushvalue(L, 3);
  1360. cbd->cbref = luaL_ref(L, LUA_REGISTRYINDEX);
  1361. cbd->ctx = ctx;
  1362. cbd->key = NULL;
  1363. if (dkim_module_ctx->dkim_hash) {
  1364. key = rspamd_lru_hash_lookup(dkim_module_ctx->dkim_hash,
  1365. rspamd_dkim_get_dns_key(ctx),
  1366. task->task_timestamp);
  1367. }
  1368. else {
  1369. key = NULL;
  1370. }
  1371. if (key != NULL) {
  1372. cbd->key = rspamd_dkim_key_ref(key);
  1373. /* Release key when task is processed */
  1374. rspamd_mempool_add_destructor(task->task_pool,
  1375. dkim_module_key_dtor, cbd->key);
  1376. ret = rspamd_dkim_check(cbd->ctx, cbd->key, cbd->task);
  1377. dkim_module_lua_push_verify_result(cbd, ret, NULL);
  1378. }
  1379. else {
  1380. rspamd_get_dkim_key(ctx,
  1381. task,
  1382. dkim_module_lua_on_key,
  1383. cbd);
  1384. }
  1385. }
  1386. else {
  1387. return luaL_error(L, "invalid arguments");
  1388. }
  1389. lua_pushboolean(L, TRUE);
  1390. lua_pushnil(L);
  1391. return 2;
  1392. }
  1393. static int
  1394. lua_dkim_canonicalize_handler(lua_State *L)
  1395. {
  1396. gsize nlen, vlen;
  1397. const char *hname = luaL_checklstring(L, 1, &nlen),
  1398. *hvalue = luaL_checklstring(L, 2, &vlen);
  1399. static char st_buf[8192];
  1400. char *buf;
  1401. unsigned int inlen;
  1402. gboolean allocated = FALSE;
  1403. goffset r;
  1404. if (hname && hvalue && nlen > 0) {
  1405. inlen = nlen + vlen + sizeof(":" CRLF);
  1406. if (inlen > sizeof(st_buf)) {
  1407. buf = g_malloc(inlen);
  1408. allocated = TRUE;
  1409. }
  1410. else {
  1411. /* Faster */
  1412. buf = st_buf;
  1413. }
  1414. r = rspamd_dkim_canonize_header_relaxed_str(hname, hvalue, buf, inlen);
  1415. if (r == -1) {
  1416. lua_pushnil(L);
  1417. }
  1418. else {
  1419. lua_pushlstring(L, buf, r);
  1420. }
  1421. if (allocated) {
  1422. g_free(buf);
  1423. }
  1424. }
  1425. else {
  1426. return luaL_error(L, "invalid arguments");
  1427. }
  1428. return 1;
  1429. }