123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620 |
- /*
- * Copyright 2024 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 char *M = "rspamd dkim plugin";
-
- static const char 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 char 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 char *symbol_reject;
- const char *symbol_tempfail;
- const char *symbol_allow;
- const char *symbol_na;
- const char *symbol_permfail;
-
- struct rspamd_radix_map_helper *whitelist_ip;
- struct rspamd_hash_map_helper *dkim_domains;
- unsigned int strict_multiplier;
- unsigned int time_jitter;
- rspamd_lru_hash_t *dkim_hash;
- rspamd_lru_hash_t *dkim_sign_hash;
- const char *sign_headers;
- const char *arc_sign_headers;
- unsigned int 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;
- double mult_allow;
- double 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 int lua_dkim_sign_handler(lua_State *L);
- static int lua_dkim_verify_handler(lua_State *L);
- static int lua_dkim_canonicalize_handler(lua_State *L);
-
- /* Initialization */
- int dkim_module_init(struct rspamd_config *cfg, struct module_ctx **ctx);
- int dkim_module_config(struct rspamd_config *cfg, bool validate);
- int 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,
- (unsigned int) -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);
- }
-
- int 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;
- }
-
- int dkim_module_config(struct rspamd_config *cfg, bool validate)
- {
- const ucl_object_t *value;
- int res = TRUE, cb_id = -1;
- unsigned int 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 char *key, gsize keylen,
- enum rspamd_dkim_key_format key_format)
-
- {
- unsigned char 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 int
- lua_dkim_sign_handler(lua_State *L)
- {
- struct rspamd_task *task = lua_check_task(L, 1);
- int64_t arc_idx = 0, expire = 0;
- enum rspamd_dkim_type sign_type = RSPAMD_DKIM_NORMAL;
- GError *err = NULL;
- GString *hdr;
- GList *sigs = NULL;
- const char *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;
- }
-
- int 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 char *value, double *allow, double *deny)
- {
- const char *colon;
- char *err = NULL;
- double val;
- char 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 char *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 char *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;
- unsigned int 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 char *symbol = NULL, *trace = NULL;
- double 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 char *domain = rspamd_dkim_get_domain(cur->ctx);
- const char *selector = rspamd_dkim_get_selector(cur->ctx);
- gsize tracelen;
- char *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;
- unsigned int checked = 0;
- double *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 char *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;
- int 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 char *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 int
- lua_dkim_verify_handler(lua_State *L)
- {
- struct rspamd_task *task = lua_check_task(L, 1);
- const char *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 char *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 int
- lua_dkim_canonicalize_handler(lua_State *L)
- {
- gsize nlen, vlen;
- const char *hname = luaL_checklstring(L, 1, &nlen),
- *hvalue = luaL_checklstring(L, 2, &vlen);
- static char st_buf[8192];
- char *buf;
- unsigned int 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;
- }
|