12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622 |
- /*-
- * Copyright 2016 Vsevolod Stakhov
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- /***MODULE:dkim
- * rspamd module that checks dkim records of incoming email
- *
- * Allowed options:
- * - symbol_allow (string): symbol to insert in case of allow (default: 'R_DKIM_ALLOW')
- * - symbol_reject (string): symbol to insert (default: 'R_DKIM_REJECT')
- * - symbol_tempfail (string): symbol to insert in case of temporary fail (default: 'R_DKIM_TEMPFAIL')
- * - symbol_permfail (string): symbol to insert in case of permanent failure (default: 'R_DKIM_PERMFAIL')
- * - symbol_na (string): symbol to insert in case of no signing (default: 'R_DKIM_NA')
- * - whitelist (map): map of whitelisted networks
- * - domains (map): map of domains to check
- * - strict_multiplier (number): multiplier for strict domains
- * - time_jitter (number): jitter in seconds to allow time diff while checking
- * - trusted_only (flag): check signatures only for domains in 'domains' map
- */
-
-
- #include "config.h"
- #include "libmime/message.h"
- #include "libserver/dkim.h"
- #include "libutil/hash.h"
- #include "libserver/maps/map.h"
- #include "libserver/maps/map_helpers.h"
- #include "rspamd.h"
- #include "utlist.h"
- #include "unix-std.h"
- #include "lua/lua_common.h"
- #include "libserver/mempool_vars_internal.h"
-
- #define DEFAULT_SYMBOL_REJECT "R_DKIM_REJECT"
- #define DEFAULT_SYMBOL_TEMPFAIL "R_DKIM_TEMPFAIL"
- #define DEFAULT_SYMBOL_ALLOW "R_DKIM_ALLOW"
- #define DEFAULT_SYMBOL_NA "R_DKIM_NA"
- #define DEFAULT_SYMBOL_PERMFAIL "R_DKIM_PERMFAIL"
- #define DEFAULT_CACHE_SIZE 2048
- #define DEFAULT_TIME_JITTER 60
- #define DEFAULT_MAX_SIGS 5
-
- static const gchar *M = "rspamd dkim plugin";
-
- static const gchar default_sign_headers[] = ""
- "(o)from:(x)sender:(o)reply-to:(o)subject:(x)date:(x)message-id:"
- "(o)to:(o)cc:(x)mime-version:(x)content-type:(x)content-transfer-encoding:"
- "resent-to:resent-cc:resent-from:resent-sender:resent-message-id:"
- "(x)in-reply-to:(x)references:list-id:list-help:list-owner:list-unsubscribe:"
- "list-unsubscribe-post:list-subscribe:list-post:(x)openpgp:(x)autocrypt";
- static const gchar default_arc_sign_headers[] = ""
- "(o)from:(x)sender:(o)reply-to:(o)subject:(x)date:(x)message-id:"
- "(o)to:(o)cc:(x)mime-version:(x)content-type:(x)content-transfer-encoding:"
- "resent-to:resent-cc:resent-from:resent-sender:resent-message-id:"
- "(x)in-reply-to:(x)references:list-id:list-help:list-owner:list-unsubscribe:"
- "list-unsubscribe-post:list-subscribe:list-post:dkim-signature:(x)openpgp:"
- "(x)autocrypt";
-
- struct dkim_ctx {
- struct module_ctx ctx;
- const gchar *symbol_reject;
- const gchar *symbol_tempfail;
- const gchar *symbol_allow;
- const gchar *symbol_na;
- const gchar *symbol_permfail;
-
- struct rspamd_radix_map_helper *whitelist_ip;
- struct rspamd_hash_map_helper *dkim_domains;
- guint strict_multiplier;
- guint time_jitter;
- rspamd_lru_hash_t *dkim_hash;
- rspamd_lru_hash_t *dkim_sign_hash;
- const gchar *sign_headers;
- const gchar *arc_sign_headers;
- guint max_sigs;
- gboolean trusted_only;
- gboolean check_local;
- gboolean check_authed;
- };
-
- struct dkim_check_result {
- rspamd_dkim_context_t *ctx;
- rspamd_dkim_key_t *key;
- struct rspamd_task *task;
- struct rspamd_dkim_check_result *res;
- gdouble mult_allow;
- gdouble mult_deny;
- struct rspamd_symcache_dynamic_item *item;
- struct dkim_check_result *next, *prev, *first;
- };
-
- static void dkim_symbol_callback (struct rspamd_task *task,
- struct rspamd_symcache_dynamic_item *item,
- void *unused);
- static void dkim_sign_callback (struct rspamd_task *task,
- struct rspamd_symcache_dynamic_item *item,
- void *unused);
-
- static gint lua_dkim_sign_handler (lua_State *L);
- static gint lua_dkim_verify_handler (lua_State *L);
- static gint lua_dkim_canonicalize_handler (lua_State *L);
-
- /* Initialization */
- gint dkim_module_init (struct rspamd_config *cfg, struct module_ctx **ctx);
- gint dkim_module_config (struct rspamd_config *cfg, bool validate);
- gint dkim_module_reconfig (struct rspamd_config *cfg);
-
- module_t dkim_module = {
- "dkim",
- dkim_module_init,
- dkim_module_config,
- dkim_module_reconfig,
- NULL,
- RSPAMD_MODULE_VER,
- (guint)-1,
- };
-
- static inline struct dkim_ctx *
- dkim_get_context (struct rspamd_config *cfg)
- {
- return (struct dkim_ctx *)g_ptr_array_index (cfg->c_modules,
- dkim_module.ctx_offset);
- }
-
- static void
- dkim_module_key_dtor (gpointer k)
- {
- rspamd_dkim_key_t *key = k;
-
- rspamd_dkim_key_unref (key);
- }
-
- static void
- dkim_module_free_list (gpointer k)
- {
- g_list_free_full ((GList *)k, rspamd_gstring_free_hard);
- }
-
- gint
- dkim_module_init (struct rspamd_config *cfg, struct module_ctx **ctx)
- {
- struct dkim_ctx *dkim_module_ctx;
-
- dkim_module_ctx = rspamd_mempool_alloc0 (cfg->cfg_pool,
- sizeof (*dkim_module_ctx));
- dkim_module_ctx->sign_headers = default_sign_headers;
- dkim_module_ctx->arc_sign_headers = default_arc_sign_headers;
- dkim_module_ctx->max_sigs = DEFAULT_MAX_SIGS;
-
- *ctx = (struct module_ctx *)dkim_module_ctx;
-
- rspamd_rcl_add_doc_by_path (cfg,
- NULL,
- "DKIM check plugin",
- "dkim",
- UCL_OBJECT,
- NULL,
- 0,
- NULL,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Map of IP addresses that should be excluded from DKIM checks",
- "whitelist",
- UCL_STRING,
- NULL,
- 0,
- NULL,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Symbol that is added if DKIM check is successful",
- "symbol_allow",
- UCL_STRING,
- NULL,
- 0,
- DEFAULT_SYMBOL_ALLOW,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Symbol that is added if DKIM check is unsuccessful",
- "symbol_reject",
- UCL_STRING,
- NULL,
- 0,
- DEFAULT_SYMBOL_REJECT,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Symbol that is added if DKIM check can't be completed (e.g. DNS failure)",
- "symbol_tempfail",
- UCL_STRING,
- NULL,
- 0,
- DEFAULT_SYMBOL_TEMPFAIL,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Symbol that is added if mail is not signed",
- "symbol_na",
- UCL_STRING,
- NULL,
- 0,
- DEFAULT_SYMBOL_NA,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Symbol that is added if permanent failure encountered",
- "symbol_permfail",
- UCL_STRING,
- NULL,
- 0,
- DEFAULT_SYMBOL_PERMFAIL,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Size of DKIM keys cache",
- "dkim_cache_size",
- UCL_INT,
- NULL,
- 0,
- G_STRINGIFY (DEFAULT_CACHE_SIZE),
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Allow this time difference when checking DKIM signature time validity",
- "time_jitter",
- UCL_TIME,
- NULL,
- 0,
- G_STRINGIFY (DEFAULT_TIME_JITTER),
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Domains to check DKIM for (check all domains if this option is empty)",
- "domains",
- UCL_STRING,
- NULL,
- 0,
- "empty",
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Map of domains that are treated as 'trusted' meaning that DKIM policy failure has more significant score",
- "trusted_domains",
- UCL_STRING,
- NULL,
- 0,
- "empty",
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Multiply dkim score by this factor for trusted domains",
- "strict_multiplier",
- UCL_FLOAT,
- NULL,
- 0,
- NULL,
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Check DKIM policies merely for `trusted_domains`",
- "trusted_only",
- UCL_BOOLEAN,
- NULL,
- 0,
- "false",
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Lua script that tells if a message should be signed and with what params (obsoleted)",
- "sign_condition",
- UCL_STRING,
- NULL,
- 0,
- "empty",
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Obsoleted: maximum number of DKIM signatures to check",
- "max_sigs",
- UCL_INT,
- NULL,
- 0,
- "n/a",
- 0);
- rspamd_rcl_add_doc_by_path (cfg,
- "dkim",
- "Headers used in signing",
- "sign_headers",
- UCL_STRING,
- NULL,
- 0,
- default_sign_headers,
- 0);
-
- return 0;
- }
-
- gint
- dkim_module_config (struct rspamd_config *cfg, bool validate)
- {
- const ucl_object_t *value;
- gint res = TRUE, cb_id = -1;
- guint cache_size, sign_cache_size;
- gboolean got_trusted = FALSE;
- struct dkim_ctx *dkim_module_ctx = dkim_get_context (cfg);
-
- /* Register global methods */
- lua_getglobal (cfg->lua_state, "rspamd_plugins");
-
- if (lua_type (cfg->lua_state, -1) == LUA_TTABLE) {
- lua_pushstring (cfg->lua_state, "dkim");
- lua_createtable (cfg->lua_state, 0, 1);
- /* Set methods */
- lua_pushstring (cfg->lua_state, "sign");
- lua_pushcfunction (cfg->lua_state, lua_dkim_sign_handler);
- lua_settable (cfg->lua_state, -3);
- lua_pushstring (cfg->lua_state, "verify");
- lua_pushcfunction (cfg->lua_state, lua_dkim_verify_handler);
- lua_settable (cfg->lua_state, -3);
- lua_pushstring (cfg->lua_state, "canon_header_relaxed");
- lua_pushcfunction (cfg->lua_state, lua_dkim_canonicalize_handler);
- lua_settable (cfg->lua_state, -3);
- /* Finish dkim key */
- lua_settable (cfg->lua_state, -3);
- }
-
- lua_pop (cfg->lua_state, 1); /* Remove global function */
- dkim_module_ctx->whitelist_ip = NULL;
-
- value = rspamd_config_get_module_opt (cfg, "dkim", "check_local");
-
- if (value == NULL) {
- value = rspamd_config_get_module_opt (cfg, "options", "check_local");
- }
-
- if (value != NULL) {
- dkim_module_ctx->check_local = ucl_object_toboolean (value);
- }
- else {
- dkim_module_ctx->check_local = FALSE;
- }
-
- value = rspamd_config_get_module_opt (cfg, "dkim",
- "check_authed");
-
- if (value == NULL) {
- value = rspamd_config_get_module_opt (cfg, "options",
- "check_authed");
- }
-
- if (value != NULL) {
- dkim_module_ctx->check_authed = ucl_object_toboolean (value);
- }
- else {
- dkim_module_ctx->check_authed = FALSE;
- }
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "symbol_reject")) != NULL) {
- dkim_module_ctx->symbol_reject = ucl_object_tostring (value);
- }
- else {
- dkim_module_ctx->symbol_reject = DEFAULT_SYMBOL_REJECT;
- }
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim",
- "symbol_tempfail")) != NULL) {
- dkim_module_ctx->symbol_tempfail = ucl_object_tostring (value);
- }
- else {
- dkim_module_ctx->symbol_tempfail = DEFAULT_SYMBOL_TEMPFAIL;
- }
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "symbol_allow")) != NULL) {
- dkim_module_ctx->symbol_allow = ucl_object_tostring (value);
- }
- else {
- dkim_module_ctx->symbol_allow = DEFAULT_SYMBOL_ALLOW;
- }
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "symbol_na")) != NULL) {
- dkim_module_ctx->symbol_na = ucl_object_tostring (value);
- }
- else {
- dkim_module_ctx->symbol_na = DEFAULT_SYMBOL_NA;
- }
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "symbol_permfail")) != NULL) {
- dkim_module_ctx->symbol_permfail = ucl_object_tostring (value);
- }
- else {
- dkim_module_ctx->symbol_permfail = DEFAULT_SYMBOL_PERMFAIL;
- }
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim",
- "dkim_cache_size")) != NULL) {
- cache_size = ucl_object_toint (value);
- }
- else {
- cache_size = DEFAULT_CACHE_SIZE;
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim",
- "sign_cache_size")) != NULL) {
- sign_cache_size = ucl_object_toint (value);
- }
- else {
- sign_cache_size = 128;
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "time_jitter")) != NULL) {
- dkim_module_ctx->time_jitter = ucl_object_todouble (value);
- }
- else {
- dkim_module_ctx->time_jitter = DEFAULT_TIME_JITTER;
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "max_sigs")) != NULL) {
- dkim_module_ctx->max_sigs = ucl_object_toint (value);
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "whitelist")) != NULL) {
-
- rspamd_config_radix_from_ucl (cfg, value, "DKIM whitelist",
- &dkim_module_ctx->whitelist_ip, NULL, NULL, "dkim whitelist");
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "domains")) != NULL) {
- if (!rspamd_map_add_from_ucl (cfg, value,
- "DKIM domains",
- rspamd_kv_list_read,
- rspamd_kv_list_fin,
- rspamd_kv_list_dtor,
- (void **)&dkim_module_ctx->dkim_domains,
- NULL, RSPAMD_MAP_DEFAULT)) {
- msg_warn_config ("cannot load dkim domains list from %s",
- ucl_object_tostring (value));
- }
- else {
- got_trusted = TRUE;
- }
- }
-
- if (!got_trusted && (value =
- rspamd_config_get_module_opt (cfg, "dkim", "trusted_domains")) != NULL) {
- if (!rspamd_map_add_from_ucl (cfg, value,
- "DKIM domains",
- rspamd_kv_list_read,
- rspamd_kv_list_fin,
- rspamd_kv_list_dtor,
- (void **)&dkim_module_ctx->dkim_domains,
- NULL, RSPAMD_MAP_DEFAULT)) {
- msg_warn_config ("cannot load dkim domains list from %s",
- ucl_object_tostring (value));
-
- if (validate) {
- return FALSE;
- }
- }
- else {
- got_trusted = TRUE;
- }
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim",
- "strict_multiplier")) != NULL) {
- dkim_module_ctx->strict_multiplier = ucl_object_toint (value);
- }
- else {
- dkim_module_ctx->strict_multiplier = 1;
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "trusted_only")) != NULL) {
- dkim_module_ctx->trusted_only = ucl_object_toboolean (value);
- }
- else {
- dkim_module_ctx->trusted_only = FALSE;
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "dkim", "sign_headers")) != NULL) {
- dkim_module_ctx->sign_headers = ucl_object_tostring (value);
- }
-
- if ((value =
- rspamd_config_get_module_opt (cfg, "arc", "sign_headers")) != NULL) {
- dkim_module_ctx->arc_sign_headers = ucl_object_tostring (value);
- }
-
- if (cache_size > 0) {
- dkim_module_ctx->dkim_hash = rspamd_lru_hash_new (
- cache_size,
- g_free,
- dkim_module_key_dtor);
- rspamd_mempool_add_destructor (cfg->cfg_pool,
- (rspamd_mempool_destruct_t)rspamd_lru_hash_destroy,
- dkim_module_ctx->dkim_hash);
- }
-
- if (sign_cache_size > 0) {
- dkim_module_ctx->dkim_sign_hash = rspamd_lru_hash_new (
- sign_cache_size,
- g_free,
- (GDestroyNotify) rspamd_dkim_sign_key_unref);
- rspamd_mempool_add_destructor (cfg->cfg_pool,
- (rspamd_mempool_destruct_t)rspamd_lru_hash_destroy,
- dkim_module_ctx->dkim_sign_hash);
- }
-
- if (dkim_module_ctx->trusted_only && !got_trusted) {
- msg_err_config ("trusted_only option is set and no trusted domains are defined");
- if (validate) {
- return FALSE;
- }
- }
- else {
- if (!rspamd_config_is_module_enabled (cfg, "dkim")) {
- return TRUE;
- }
-
- cb_id = rspamd_symcache_add_symbol (cfg->cache,
- "DKIM_CHECK",
- 0,
- dkim_symbol_callback,
- NULL,
- SYMBOL_TYPE_CALLBACK,
- -1);
- rspamd_config_add_symbol (cfg,
- "DKIM_CHECK",
- 0.0,
- "DKIM check callback",
- "policies",
- RSPAMD_SYMBOL_FLAG_IGNORE_METRIC,
- 1,
- 1);
- rspamd_config_add_symbol_group (cfg, "DKIM_CHECK", "dkim");
- rspamd_symcache_add_symbol (cfg->cache,
- dkim_module_ctx->symbol_reject,
- 0,
- NULL,
- NULL,
- SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
- cb_id);
- rspamd_symcache_add_symbol (cfg->cache,
- dkim_module_ctx->symbol_na,
- 0,
- NULL, NULL,
- SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
- cb_id);
- rspamd_symcache_add_symbol (cfg->cache,
- dkim_module_ctx->symbol_permfail,
- 0,
- NULL, NULL,
- SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
- cb_id);
- rspamd_symcache_add_symbol (cfg->cache,
- dkim_module_ctx->symbol_tempfail,
- 0,
- NULL, NULL,
- SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
- cb_id);
- rspamd_symcache_add_symbol (cfg->cache,
- dkim_module_ctx->symbol_allow,
- 0,
- NULL, NULL,
- SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_FINE,
- cb_id);
-
- rspamd_symcache_add_symbol (cfg->cache,
- "DKIM_TRACE",
- 0,
- NULL, NULL,
- SYMBOL_TYPE_VIRTUAL | SYMBOL_TYPE_NOSTAT,
- cb_id);
- rspamd_config_add_symbol (cfg,
- "DKIM_TRACE",
- 0.0,
- "DKIM trace symbol",
- "policies",
- RSPAMD_SYMBOL_FLAG_IGNORE_METRIC,
- 1,
- 1);
- rspamd_config_add_symbol_group (cfg, "DKIM_TRACE", "dkim");
-
- msg_info_config ("init internal dkim module");
- #ifndef HAVE_OPENSSL
- msg_warn_config (
- "openssl is not found so dkim rsa check is disabled, only check body hash, it is NOT safe to trust these results");
- #endif
- }
-
- return res;
- }
-
-
- /**
- * Grab a private key from the cache
- * or from the key content provided
- */
- rspamd_dkim_sign_key_t *
- dkim_module_load_key_format (struct rspamd_task *task,
- struct dkim_ctx *dkim_module_ctx,
- const gchar *key, gsize keylen,
- enum rspamd_dkim_key_format key_format)
-
- {
- guchar h[rspamd_cryptobox_HASHBYTES],
- hex_hash[rspamd_cryptobox_HASHBYTES * 2 + 1];
- rspamd_dkim_sign_key_t *ret = NULL;
- GError *err = NULL;
- struct stat st;
-
- memset (hex_hash, 0, sizeof (hex_hash));
- rspamd_cryptobox_hash (h, key, keylen, NULL, 0);
- rspamd_encode_hex_buf (h, sizeof (h), hex_hash, sizeof (hex_hash));
-
- if (dkim_module_ctx->dkim_sign_hash) {
- ret = rspamd_lru_hash_lookup (dkim_module_ctx->dkim_sign_hash,
- hex_hash, time (NULL));
- }
-
- /*
- * This fails for paths that are also valid base64.
- * Maybe the caller should have specified a format.
- */
- if (key_format == RSPAMD_DKIM_KEY_UNKNOWN) {
- if (key[0] == '.' || key[0] == '/') {
- if (!rspamd_cryptobox_base64_is_valid (key, keylen)) {
- key_format = RSPAMD_DKIM_KEY_FILE;
- }
- }
- else if (rspamd_cryptobox_base64_is_valid (key, keylen)) {
- key_format = RSPAMD_DKIM_KEY_BASE64;
- }
- }
-
-
- if (ret != NULL && key_format == RSPAMD_DKIM_KEY_FILE) {
- msg_debug_task("checking for stale file key");
-
- if (stat (key, &st) != 0) {
- msg_err_task("cannot stat key file: %s", strerror (errno));
- return NULL;
- }
-
- if (rspamd_dkim_sign_key_maybe_invalidate (ret, st.st_mtime)) {
- msg_debug_task("removing stale file key");
- /*
- * Invalidate DKIM key
- * removal from lru cache also cleanup the key and value
- */
- if (dkim_module_ctx->dkim_sign_hash) {
- rspamd_lru_hash_remove (dkim_module_ctx->dkim_sign_hash,
- hex_hash);
- }
- ret = NULL;
- }
- }
-
- /* found key; done */
- if (ret != NULL) {
- return ret;
- }
-
- ret = rspamd_dkim_sign_key_load (key, keylen, key_format, &err);
-
- if (ret == NULL) {
- msg_err_task ("cannot load dkim key %s: %e",
- key, err);
- g_error_free (err);
- }
- else if (dkim_module_ctx->dkim_sign_hash) {
- rspamd_lru_hash_insert (dkim_module_ctx->dkim_sign_hash,
- g_strdup (hex_hash), ret, time (NULL), 0);
- }
-
- return ret;
- }
-
- static gint
- lua_dkim_sign_handler (lua_State *L)
- {
- struct rspamd_task *task = lua_check_task (L, 1);
- gint64 arc_idx = 0, expire = 0;
- enum rspamd_dkim_type sign_type = RSPAMD_DKIM_NORMAL;
- GError *err = NULL;
- GString *hdr;
- GList *sigs = NULL;
- const gchar *selector = NULL, *domain = NULL, *key = NULL, *rawkey = NULL,
- *headers = NULL, *sign_type_str = NULL, *arc_cv = NULL,
- *pubkey = NULL;
- rspamd_dkim_sign_context_t *ctx;
- rspamd_dkim_sign_key_t *dkim_key;
- gsize rawlen = 0, keylen = 0;
- gboolean no_cache = FALSE, strict_pubkey_check = FALSE;
- struct dkim_ctx *dkim_module_ctx;
-
- luaL_argcheck (L, lua_type (L, 2) == LUA_TTABLE, 2, "'table' expected");
- /*
- * Get the following elements:
- * - selector
- * - domain
- * - key
- */
- if (!rspamd_lua_parse_table_arguments (L, 2, &err,
- RSPAMD_LUA_PARSE_ARGUMENTS_DEFAULT,
- "key=V;rawkey=V;*domain=S;*selector=S;no_cache=B;headers=S;"
- "sign_type=S;arc_idx=I;arc_cv=S;expire=I;pubkey=S;"
- "strict_pubkey_check=B",
- &keylen, &key, &rawlen, &rawkey, &domain,
- &selector, &no_cache, &headers,
- &sign_type_str, &arc_idx, &arc_cv, &expire, &pubkey,
- &strict_pubkey_check)) {
- msg_err_task ("cannot parse table arguments: %e",
- err);
- g_error_free (err);
-
- lua_pushboolean (L, FALSE);
- return 1;
- }
-
- dkim_module_ctx = dkim_get_context (task->cfg);
-
- if (key) {
- dkim_key = dkim_module_load_key_format (task, dkim_module_ctx, key,
- keylen, RSPAMD_DKIM_KEY_UNKNOWN);
- }
- else if (rawkey) {
- dkim_key = dkim_module_load_key_format (task, dkim_module_ctx, rawkey,
- rawlen, RSPAMD_DKIM_KEY_UNKNOWN);
- }
- else {
- msg_err_task ("neither key nor rawkey are specified");
- lua_pushboolean (L, FALSE);
-
- return 1;
- }
-
- if (dkim_key == NULL) {
- lua_pushboolean (L, FALSE);
- return 1;
- }
-
- if (sign_type_str) {
- if (strcmp (sign_type_str, "dkim") == 0) {
- sign_type = RSPAMD_DKIM_NORMAL;
-
- if (headers == NULL) {
- headers = dkim_module_ctx->sign_headers;
- }
- }
- else if (strcmp (sign_type_str, "arc-sign") == 0) {
- sign_type = RSPAMD_DKIM_ARC_SIG;
-
- if (headers == NULL) {
- headers = dkim_module_ctx->arc_sign_headers;
- }
-
- if (arc_idx == 0) {
- lua_settop (L, 0);
- return luaL_error (L, "no arc idx specified");
- }
- }
- else if (strcmp (sign_type_str, "arc-seal") == 0) {
- sign_type = RSPAMD_DKIM_ARC_SEAL;
- if (arc_cv == NULL) {
- lua_settop (L, 0);
- return luaL_error (L, "no arc cv specified");
- }
- if (arc_idx == 0) {
- lua_settop (L, 0);
- return luaL_error (L, "no arc idx specified");
- }
- }
- else {
- lua_settop (L, 0);
- return luaL_error (L, "unknown sign type: %s",
- sign_type_str);
- }
- }
- else {
- /* Unspecified sign type, assume plain dkim */
- if (headers == NULL) {
- headers = dkim_module_ctx->sign_headers;
- }
- }
-
- if (pubkey != NULL) {
- /* Also check if private and public keys match */
- rspamd_dkim_key_t *pk;
- keylen = strlen (pubkey);
-
- pk = rspamd_dkim_parse_key (pubkey, &keylen, NULL);
-
- if (pk == NULL) {
- if (strict_pubkey_check) {
- msg_err_task ("cannot parse pubkey from string: %s, skip signing",
- pubkey);
- lua_pushboolean (L, FALSE);
-
- return 1;
- }
- else {
- msg_warn_task ("cannot parse pubkey from string: %s",
- pubkey);
- }
- }
- else {
- GError *te = NULL;
-
- /* We have parsed the key, so try to check keys */
- if (!rspamd_dkim_match_keys (pk, dkim_key, &te)) {
- if (strict_pubkey_check) {
- msg_err_task ("public key for %s/%s does not match private "
- "key: %e, skip signing",
- domain, selector, te);
- g_error_free (te);
- lua_pushboolean (L, FALSE);
- rspamd_dkim_key_unref (pk);
-
- return 1;
- }
- else {
- msg_warn_task ("public key for %s/%s does not match private "
- "key: %e",
- domain, selector, te);
- g_error_free (te);
- }
- }
-
- rspamd_dkim_key_unref (pk);
- }
- }
-
- ctx = rspamd_create_dkim_sign_context (task, dkim_key,
- DKIM_CANON_RELAXED, DKIM_CANON_RELAXED,
- headers, sign_type, &err);
-
- if (ctx == NULL) {
- msg_err_task ("cannot create sign context: %e",
- err);
- g_error_free (err);
-
- lua_pushboolean (L, FALSE);
- return 1;
- }
-
- hdr = rspamd_dkim_sign (task, selector, domain, 0,
- expire, arc_idx, arc_cv, ctx);
-
- if (hdr) {
-
- if (!no_cache) {
- sigs = rspamd_mempool_get_variable (task->task_pool, "dkim-signature");
-
- if (sigs == NULL) {
- sigs = g_list_append (sigs, hdr);
- rspamd_mempool_set_variable (task->task_pool, "dkim-signature",
- sigs, dkim_module_free_list);
- } else {
- sigs = g_list_append (sigs, hdr);
- (void)sigs;
- }
- }
-
- lua_pushboolean (L, TRUE);
- lua_pushlstring (L, hdr->str, hdr->len);
-
- if (no_cache) {
- g_string_free (hdr, TRUE);
- }
-
- return 2;
- }
-
-
- lua_pushboolean (L, FALSE);
- lua_pushnil (L);
-
- return 2;
- }
-
- gint
- dkim_module_reconfig (struct rspamd_config *cfg)
- {
- return dkim_module_config (cfg, false);
- }
-
- /*
- * Parse strict value for domain in format: 'reject_multiplier:deny_multiplier'
- */
- static gboolean
- dkim_module_parse_strict (const gchar *value, gdouble *allow, gdouble *deny)
- {
- const gchar *colon;
- gchar *err = NULL;
- gdouble val;
- gchar numbuf[64];
-
- colon = strchr (value, ':');
- if (colon) {
- rspamd_strlcpy (numbuf, value,
- MIN (sizeof (numbuf), (colon - value) + 1));
- val = strtod (numbuf, &err);
-
- if (err == NULL || *err == '\0') {
- *deny = val;
- colon++;
- rspamd_strlcpy (numbuf, colon, sizeof (numbuf));
- err = NULL;
- val = strtod (numbuf, &err);
-
- if (err == NULL || *err == '\0') {
- *allow = val;
- return TRUE;
- }
- }
- }
- return FALSE;
- }
-
- static void
- dkim_module_check (struct dkim_check_result *res)
- {
- gboolean all_done = TRUE;
- const gchar *strict_value;
- struct dkim_check_result *first, *cur = NULL;
- struct dkim_ctx *dkim_module_ctx = dkim_get_context (res->task->cfg);
- struct rspamd_task *task = res->task;
-
- first = res->first;
-
- DL_FOREACH (first, cur) {
- if (cur->ctx == NULL) {
- continue;
- }
-
- if (cur->key != NULL && cur->res == NULL) {
- cur->res = rspamd_dkim_check (cur->ctx, cur->key, task);
-
- if (dkim_module_ctx->dkim_domains != NULL) {
- /* Perform strict check */
- const gchar *domain = rspamd_dkim_get_domain (cur->ctx);
-
- if ((strict_value =
- rspamd_match_hash_map (dkim_module_ctx->dkim_domains,
- domain,
- strlen (domain))) != NULL) {
- if (!dkim_module_parse_strict (strict_value, &cur->mult_allow,
- &cur->mult_deny)) {
- cur->mult_allow = dkim_module_ctx->strict_multiplier;
- cur->mult_deny = dkim_module_ctx->strict_multiplier;
- }
- }
- }
- }
- }
-
- DL_FOREACH (first, cur) {
- if (cur->ctx == NULL) {
- continue;
- }
- if (cur->res == NULL) {
- /* Still need a key */
- all_done = FALSE;
- }
- }
-
- if (all_done) {
- /* Create zero terminated array of results */
- struct rspamd_dkim_check_result **pres;
- guint nres = 0, i = 0;
-
- DL_FOREACH (first, cur) {
- if (cur->ctx == NULL || cur->res == NULL) {
- continue;
- }
-
- nres ++;
- }
-
- pres = rspamd_mempool_alloc (task->task_pool, sizeof (*pres) * (nres + 1));
- pres[nres] = NULL;
-
- DL_FOREACH (first, cur) {
- const gchar *symbol = NULL, *trace = NULL;
- gdouble symbol_weight = 1.0;
-
- if (cur->ctx == NULL || cur->res == NULL) {
- continue;
- }
-
- pres[i++] = cur->res;
-
- if (cur->res->rcode == DKIM_REJECT) {
- symbol = dkim_module_ctx->symbol_reject;
- trace = "-";
- symbol_weight = cur->mult_deny * 1.0;
- }
- else if (cur->res->rcode == DKIM_CONTINUE) {
- symbol = dkim_module_ctx->symbol_allow;
- trace = "+";
- symbol_weight = cur->mult_allow * 1.0;
- }
- else if (cur->res->rcode == DKIM_PERM_ERROR) {
- trace = "~";
- symbol = dkim_module_ctx->symbol_permfail;
- }
- else if (cur->res->rcode == DKIM_TRYAGAIN) {
- trace = "?";
- symbol = dkim_module_ctx->symbol_tempfail;
- }
-
- if (symbol != NULL) {
- const gchar *domain = rspamd_dkim_get_domain (cur->ctx);
- const gchar *selector = rspamd_dkim_get_selector (cur->ctx);
- gsize tracelen;
- gchar *tracebuf;
-
- tracelen = strlen (domain) + strlen (selector) + 4;
- tracebuf = rspamd_mempool_alloc (task->task_pool,
- tracelen);
- rspamd_snprintf (tracebuf, tracelen, "%s:%s", domain, trace);
-
- rspamd_task_insert_result (cur->task,
- "DKIM_TRACE",
- 0.0,
- tracebuf);
-
- rspamd_snprintf (tracebuf, tracelen, "%s:s=%s", domain, selector);
- rspamd_task_insert_result (task,
- symbol,
- symbol_weight,
- tracebuf);
- }
-
- }
-
- rspamd_mempool_set_variable (task->task_pool,
- RSPAMD_MEMPOOL_DKIM_CHECK_RESULTS,
- pres, NULL);
- }
- }
-
- static void
- dkim_module_key_handler (rspamd_dkim_key_t *key,
- gsize keylen,
- rspamd_dkim_context_t *ctx,
- gpointer ud,
- GError *err)
- {
- struct dkim_check_result *res = ud;
- struct rspamd_task *task;
- struct dkim_ctx *dkim_module_ctx;
-
- task = res->task;
- dkim_module_ctx = dkim_get_context (task->cfg);
-
- if (key != NULL) {
- /* Another ref belongs to the check context */
- res->key = rspamd_dkim_key_ref (key);
- /*
- * We actually receive key with refcount = 1, so we just assume that
- * lru hash owns this object now
- */
- /* Release key when task is processed */
- rspamd_mempool_add_destructor (res->task->task_pool,
- dkim_module_key_dtor, res->key);
-
- if (dkim_module_ctx->dkim_hash) {
- rspamd_lru_hash_insert (dkim_module_ctx->dkim_hash,
- g_strdup (rspamd_dkim_get_dns_key (ctx)),
- key, res->task->task_timestamp, rspamd_dkim_key_get_ttl (key));
-
- msg_info_task ("stored DKIM key for %s in LRU cache for %d seconds, "
- "%d/%d elements in the cache",
- rspamd_dkim_get_dns_key (ctx),
- rspamd_dkim_key_get_ttl (key),
- rspamd_lru_hash_size (dkim_module_ctx->dkim_hash),
- rspamd_lru_hash_capacity (dkim_module_ctx->dkim_hash));
- }
- }
- else {
- /* Insert tempfail symbol */
- msg_info_task ("cannot get key for domain %s: %e",
- rspamd_dkim_get_dns_key (ctx), err);
-
- if (err != NULL) {
- if (err->code == DKIM_SIGERROR_NOKEY) {
- res->res = rspamd_dkim_create_result (ctx, DKIM_TRYAGAIN, task);
- res->res->fail_reason = "DNS error when getting key";
- }
- else {
- res->res = rspamd_dkim_create_result (ctx, DKIM_PERM_ERROR, task);
- res->res->fail_reason = "invalid DKIM record";
- }
- }
- }
-
- if (err) {
- g_error_free (err);
- }
-
- dkim_module_check (res);
- }
-
- static void
- dkim_symbol_callback (struct rspamd_task *task,
- struct rspamd_symcache_dynamic_item *item,
- void *unused)
- {
- rspamd_dkim_context_t *ctx;
- rspamd_dkim_key_t *key;
- GError *err = NULL;
- struct rspamd_mime_header *rh, *rh_cur;
- struct dkim_check_result *res = NULL, *cur;
- guint checked = 0;
- gdouble *dmarc_checks;
- struct dkim_ctx *dkim_module_ctx = dkim_get_context (task->cfg);
-
- /* Allow dmarc */
- dmarc_checks = rspamd_mempool_get_variable (task->task_pool,
- RSPAMD_MEMPOOL_DMARC_CHECKS);
-
- if (dmarc_checks) {
- (*dmarc_checks) ++;
- }
- else {
- dmarc_checks = rspamd_mempool_alloc (task->task_pool,
- sizeof (*dmarc_checks));
- *dmarc_checks = 1;
- rspamd_mempool_set_variable (task->task_pool,
- RSPAMD_MEMPOOL_DMARC_CHECKS,
- dmarc_checks, NULL);
- }
-
- /* First check if plugin should be enabled */
- if ((!dkim_module_ctx->check_authed && task->auth_user != NULL)
- || (!dkim_module_ctx->check_local &&
- rspamd_ip_is_local_cfg (task->cfg, task->from_addr))) {
- msg_info_task ("skip DKIM checks for local networks and authorized users");
- rspamd_symcache_finalize_item (task, item);
-
- return;
- }
- /* Check whitelist */
- if (rspamd_match_radix_map_addr (dkim_module_ctx->whitelist_ip,
- task->from_addr) != NULL) {
- msg_info_task ("skip DKIM checks for whitelisted address");
- rspamd_symcache_finalize_item (task, item);
-
- return;
- }
-
- rspamd_symcache_item_async_inc (task, item, M);
-
- /* Now check if a message has its signature */
- rh = rspamd_message_get_header_array(task, RSPAMD_DKIM_SIGNHEADER, FALSE);
- if (rh) {
- msg_debug_task ("dkim signature found");
-
- DL_FOREACH (rh, rh_cur) {
- if (rh_cur->decoded == NULL || rh_cur->decoded[0] == '\0') {
- msg_info_task ("cannot load empty DKIM signature");
- continue;
- }
-
- cur = rspamd_mempool_alloc0 (task->task_pool, sizeof (*cur));
- cur->first = res;
- cur->res = NULL;
- cur->task = task;
- cur->mult_allow = 1.0;
- cur->mult_deny = 1.0;
- cur->item = item;
-
- ctx = rspamd_create_dkim_context (rh_cur->decoded,
- task->task_pool,
- task->resolver,
- dkim_module_ctx->time_jitter,
- RSPAMD_DKIM_NORMAL,
- &err);
-
- if (res == NULL) {
- res = cur;
- res->first = res;
- res->prev = res;
- }
- else {
- DL_APPEND (res, cur);
- }
-
- if (ctx == NULL) {
- if (err != NULL) {
- msg_info_task ("cannot parse DKIM signature: %e",
- err);
- g_error_free (err);
- err = NULL;
- }
- else {
- msg_info_task ("cannot parse DKIM signature: "
- "unknown error");
- }
-
- continue;
- }
- else {
- /* Get key */
- cur->ctx = ctx;
- const gchar *domain = rspamd_dkim_get_domain (cur->ctx);
-
- if (dkim_module_ctx->trusted_only &&
- (dkim_module_ctx->dkim_domains == NULL ||
- rspamd_match_hash_map (dkim_module_ctx->dkim_domains,
- domain, strlen (domain)) == NULL)) {
- msg_debug_task ("skip dkim check for %s domain",
- rspamd_dkim_get_domain (ctx));
-
- continue;
- }
-
- if (dkim_module_ctx->dkim_hash) {
- key = rspamd_lru_hash_lookup (dkim_module_ctx->dkim_hash,
- rspamd_dkim_get_dns_key (ctx),
- task->task_timestamp);
- }
- else {
- key = NULL;
- }
-
- if (key != NULL) {
- cur->key = rspamd_dkim_key_ref (key);
- /* Release key when task is processed */
- rspamd_mempool_add_destructor (task->task_pool,
- dkim_module_key_dtor, cur->key);
- }
- else {
- if (!rspamd_get_dkim_key (ctx,
- task,
- dkim_module_key_handler,
- cur)) {
- continue;
- }
- }
- }
-
- checked ++;
-
- if (checked > dkim_module_ctx->max_sigs) {
- msg_info_task ("message has multiple signatures but we"
- " stopped after %d checked signatures as limit"
- " is reached", checked);
- break;
- }
- }
- }
- else {
- rspamd_task_insert_result (task,
- dkim_module_ctx->symbol_na,
- 1.0,
- NULL);
- }
-
- if (res != NULL) {
- dkim_module_check (res);
- }
-
- rspamd_symcache_item_async_dec_check (task, item, M);
- }
-
- struct rspamd_dkim_lua_verify_cbdata {
- rspamd_dkim_context_t *ctx;
- struct rspamd_task *task;
- lua_State *L;
- rspamd_dkim_key_t *key;
- gint cbref;
- };
-
- static void
- dkim_module_lua_push_verify_result (struct rspamd_dkim_lua_verify_cbdata *cbd,
- struct rspamd_dkim_check_result *res, GError *err)
- {
- struct rspamd_task **ptask, *task;
- const gchar *error_str = "unknown error";
- gboolean success = FALSE;
-
- task = cbd->task;
-
- switch (res->rcode) {
- case DKIM_CONTINUE:
- error_str = NULL;
- success = TRUE;
- break;
- case DKIM_REJECT:
- if (err) {
- error_str = err->message;
- }
- else {
- error_str = "reject";
- }
- break;
- case DKIM_TRYAGAIN:
- if (err) {
- error_str = err->message;
- }
- else {
- error_str = "tempfail";
- }
- break;
- case DKIM_NOTFOUND:
- if (err) {
- error_str = err->message;
- }
- else {
- error_str = "not found";
- }
- break;
- case DKIM_RECORD_ERROR:
- if (err) {
- error_str = err->message;
- }
- else {
- error_str = "bad record";
- }
- break;
- case DKIM_PERM_ERROR:
- if (err) {
- error_str = err->message;
- }
- else {
- error_str = "permanent error";
- }
- break;
- default:
- break;
- }
-
- lua_rawgeti (cbd->L, LUA_REGISTRYINDEX, cbd->cbref);
- ptask = lua_newuserdata (cbd->L, sizeof (*ptask));
- *ptask = task;
- lua_pushboolean (cbd->L, success);
-
- if (error_str) {
- lua_pushstring (cbd->L, error_str);
- }
- else {
- lua_pushnil (cbd->L);
- }
-
- if (cbd->ctx) {
- if (res->domain) {
- lua_pushstring (cbd->L, res->domain);
- }
- else {
- lua_pushnil (cbd->L);
- }
-
- if (res->selector) {
- lua_pushstring (cbd->L, res->selector);
- }
- else {
- lua_pushnil (cbd->L);
- }
-
- if (res->short_b) {
- lua_pushstring (cbd->L, res->short_b);
- }
- else {
- lua_pushnil (cbd->L);
- }
-
- if (res->fail_reason) {
- lua_pushstring (cbd->L, res->fail_reason);
- }
- else {
- lua_pushnil (cbd->L);
- }
- }
- else {
- lua_pushnil (cbd->L);
- lua_pushnil (cbd->L);
- lua_pushnil (cbd->L);
- lua_pushnil (cbd->L);
- }
-
- if (lua_pcall (cbd->L, 7, 0, 0) != 0) {
- msg_err_task ("call to verify callback failed: %s",
- lua_tostring (cbd->L, -1));
- lua_pop (cbd->L, 1);
- }
-
- luaL_unref (cbd->L, LUA_REGISTRYINDEX, cbd->cbref);
- }
-
- static void
- dkim_module_lua_on_key (rspamd_dkim_key_t *key,
- gsize keylen,
- rspamd_dkim_context_t *ctx,
- gpointer ud,
- GError *err)
- {
- struct rspamd_dkim_lua_verify_cbdata *cbd = ud;
- struct rspamd_task *task;
- struct rspamd_dkim_check_result *res;
- struct dkim_ctx *dkim_module_ctx;
-
- task = cbd->task;
- dkim_module_ctx = dkim_get_context (task->cfg);
-
- if (key != NULL) {
- /* Another ref belongs to the check context */
- cbd->key = rspamd_dkim_key_ref (key);
- /*
- * We actually receive key with refcount = 1, so we just assume that
- * lru hash owns this object now
- */
-
- if (dkim_module_ctx->dkim_hash) {
- rspamd_lru_hash_insert (dkim_module_ctx->dkim_hash,
- g_strdup (rspamd_dkim_get_dns_key (ctx)),
- key, cbd->task->task_timestamp, rspamd_dkim_key_get_ttl (key));
- }
- /* Release key when task is processed */
- rspamd_mempool_add_destructor (cbd->task->task_pool,
- dkim_module_key_dtor, cbd->key);
- }
- else {
- /* Insert tempfail symbol */
- msg_info_task ("cannot get key for domain %s: %e",
- rspamd_dkim_get_dns_key (ctx), err);
-
- if (err != NULL) {
- if (err->code == DKIM_SIGERROR_NOKEY) {
- res = rspamd_dkim_create_result (ctx, DKIM_TRYAGAIN, task);
- res->fail_reason = "DNS error when getting key";
-
- }
- else {
- res = rspamd_dkim_create_result (ctx, DKIM_PERM_ERROR, task);
- res->fail_reason = "invalid DKIM record";
- }
- }
- else {
- res = rspamd_dkim_create_result (ctx, DKIM_TRYAGAIN, task);
- res->fail_reason = "DNS error when getting key";
- }
-
- dkim_module_lua_push_verify_result (cbd, res, err);
-
- if (err) {
- g_error_free (err);
- }
-
- return;
- }
-
- res = rspamd_dkim_check (cbd->ctx, cbd->key, cbd->task);
- dkim_module_lua_push_verify_result (cbd, res, NULL);
- }
-
- static gint
- lua_dkim_verify_handler (lua_State *L)
- {
- struct rspamd_task *task = lua_check_task (L, 1);
- const gchar *sig = luaL_checkstring (L, 2);
- rspamd_dkim_context_t *ctx;
- struct rspamd_dkim_lua_verify_cbdata *cbd;
- rspamd_dkim_key_t *key;
- struct rspamd_dkim_check_result *ret;
- GError *err = NULL;
- const gchar *type_str = NULL;
- enum rspamd_dkim_type type = RSPAMD_DKIM_NORMAL;
- struct dkim_ctx *dkim_module_ctx;
-
- if (task && sig && lua_isfunction (L, 3)) {
- if (lua_isstring (L, 4)) {
- type_str = lua_tostring (L, 4);
-
- if (type_str) {
- if (strcmp (type_str, "dkim") == 0) {
- type = RSPAMD_DKIM_NORMAL;
- }
- else if (strcmp (type_str, "arc-sign") == 0) {
- type = RSPAMD_DKIM_ARC_SIG;
- }
- else if (strcmp (type_str, "arc-seal") == 0) {
- type = RSPAMD_DKIM_ARC_SEAL;
- }
- else {
- lua_settop (L, 0);
- return luaL_error (L, "unknown sign type: %s",
- type_str);
- }
- }
- }
-
- dkim_module_ctx = dkim_get_context (task->cfg);
-
- ctx = rspamd_create_dkim_context (sig,
- task->task_pool,
- task->resolver,
- dkim_module_ctx->time_jitter,
- type,
- &err);
-
- if (ctx == NULL) {
- lua_pushboolean (L, false);
-
- if (err) {
- lua_pushstring (L, err->message);
- g_error_free (err);
- }
- else {
- lua_pushstring (L, "unknown error");
- }
-
- return 2;
- }
-
- cbd = rspamd_mempool_alloc (task->task_pool, sizeof (*cbd));
- cbd->L = L;
- cbd->task = task;
- lua_pushvalue (L, 3);
- cbd->cbref = luaL_ref (L, LUA_REGISTRYINDEX);
- cbd->ctx = ctx;
- cbd->key = NULL;
-
- if (dkim_module_ctx->dkim_hash) {
- key = rspamd_lru_hash_lookup (dkim_module_ctx->dkim_hash,
- rspamd_dkim_get_dns_key (ctx),
- task->task_timestamp);
- }
- else {
- key = NULL;
- }
-
- if (key != NULL) {
- cbd->key = rspamd_dkim_key_ref (key);
- /* Release key when task is processed */
- rspamd_mempool_add_destructor (task->task_pool,
- dkim_module_key_dtor, cbd->key);
- ret = rspamd_dkim_check (cbd->ctx, cbd->key, cbd->task);
- dkim_module_lua_push_verify_result (cbd, ret, NULL);
- }
- else {
- rspamd_get_dkim_key (ctx,
- task,
- dkim_module_lua_on_key,
- cbd);
- }
- }
- else {
- return luaL_error (L, "invalid arguments");
- }
-
- lua_pushboolean (L, TRUE);
- lua_pushnil (L);
-
- return 2;
- }
-
- static gint
- lua_dkim_canonicalize_handler (lua_State *L)
- {
- gsize nlen, vlen;
- const gchar *hname = luaL_checklstring (L, 1, &nlen),
- *hvalue = luaL_checklstring (L, 2, &vlen);
- static gchar st_buf[8192];
- gchar *buf;
- guint inlen;
- gboolean allocated = FALSE;
- goffset r;
-
- if (hname && hvalue && nlen > 0) {
- inlen = nlen + vlen + sizeof (":" CRLF);
-
- if (inlen > sizeof (st_buf)) {
- buf = g_malloc (inlen);
- allocated = TRUE;
- }
- else {
- /* Faster */
- buf = st_buf;
- }
-
- r = rspamd_dkim_canonize_header_relaxed_str (hname, hvalue, buf, inlen);
-
- if (r == -1) {
- lua_pushnil (L);
- }
- else {
- lua_pushlstring (L, buf, r);
- }
-
- if (allocated) {
- g_free (buf);
- }
- }
- else {
- return luaL_error (L, "invalid arguments");
- }
-
- return 1;
- }
|