diff options
author | John McKay <adenosine3p@gmail.com> | 2019-01-24 01:26:21 +0000 |
---|---|---|
committer | John McKay <adenosine3p@gmail.com> | 2019-02-02 05:41:41 +0000 |
commit | 875f80ee9aa1ff58463f32864ec5fcd57558b0a5 (patch) | |
tree | 9863ff43e6a10a08c31053690cfe00efe9d40c2b /src/plugins/dkim_check.c | |
parent | 04b5bfde88920f472516ebae5172b232e7ca37a0 (diff) | |
download | rspamd-875f80ee9aa1ff58463f32864ec5fcd57558b0a5.tar.gz rspamd-875f80ee9aa1ff58463f32864ec5fcd57558b0a5.zip |
Refactor dkim private key loads
Diffstat (limited to 'src/plugins/dkim_check.c')
-rw-r--r-- | src/plugins/dkim_check.c | 528 |
1 files changed, 261 insertions, 267 deletions
diff --git a/src/plugins/dkim_check.c b/src/plugins/dkim_check.c index 92354529f..06c039ea4 100644 --- a/src/plugins/dkim_check.c +++ b/src/plugins/dkim_check.c @@ -38,6 +38,7 @@ #include "libutil/map_helpers.h" #include "rspamd.h" #include "utlist.h" +#include "unix-std.h" #include "lua/lua_common.h" #include "libserver/mempool_vars_internal.h" @@ -634,16 +635,58 @@ dkim_module_config (struct rspamd_config *cfg) return res; } +/** + * helper to see if valid base64, minus newline + */ +static gboolean +is_valid_base64(const uint8_t *in, size_t len) { + int i; + if (in[len - 1] == '\n') + len--; + if (in[len - 1] == '\r') + len--; + if (len % 4 != 0) + return FALSE; + + if (in[len - 1] == '=') + len--; + if (in[len - 1] == '=') + len--; + + for (i = 0; i < len; i++) { + if ('a' <= in[i] && in[i] <= 'z') + continue; + if ('A' <= in[i] && in[i] <= 'Z') + continue; + if ('0' <= in[i] && in[i] <= '9') + continue; + if (in[i] == '/') + continue; + if (in[i] == '+') + continue; + return FALSE; + } + return TRUE; +} + + +#define PEM_SIG "-----BEGIN" +/** + * Grab a private key from the cache + * or from the key content provided + */ rspamd_dkim_sign_key_t * -dkim_module_load_key_format (lua_State *L, struct rspamd_task *task, - const gchar *key, gsize keylen, - enum rspamd_dkim_sign_key_type kt) +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; GError *err = NULL; - struct dkim_ctx *dkim_module_ctx = dkim_get_context (task->cfg); + gpointer map = NULL, tmp = NULL; + struct stat st; + ssize_t maplen; memset (hex_hash, 0, sizeof (hex_hash)); rspamd_cryptobox_hash (h, key, keylen, NULL, 0); @@ -651,23 +694,95 @@ dkim_module_load_key_format (lua_State *L, struct rspamd_task *task, ret = rspamd_lru_hash_lookup (dkim_module_ctx->dkim_sign_hash, hex_hash, time (NULL)); - if (ret == NULL) { - ret = rspamd_dkim_sign_key_load (key, keylen, kt, &err); + /* + * This fails for paths that are also valid base64. + * Maybe the caller should have specified a format. + */ + if (key_format == RSPAMD_DKIM_KEY_UNKNOWN && + (key[0] == '.' || key[0] == '/')) { + if (!is_valid_base64 (key, keylen)) + key_format = RSPAMD_DKIM_KEY_FILE; + } - if (ret == NULL) { - msg_err_task ("cannot load private key: %e", err); - g_error_free (err); + 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 + */ + rspamd_lru_hash_remove (dkim_module_ctx->dkim_sign_hash, + hex_hash); + ret = NULL; + } + } - return NULL; - } + /* found key; done */ + if (ret != NULL) + return ret; - rspamd_lru_hash_insert (dkim_module_ctx->dkim_sign_hash, - g_strdup (hex_hash), ret, - time (NULL), 0); + if (key_format == RSPAMD_DKIM_KEY_FILE) { + if (stat (key, &st) != 0) { + msg_err_task("cannot stat key file: %s", strerror (errno)); + return NULL; } + map = rspamd_file_xmap (key, PROT_READ, &maplen, TRUE); + if (map == NULL) { + msg_err_task("cannot open key file \'%s\'", key); + return NULL; + } + key = map; + keylen = maplen; + if (maplen > sizeof (PEM_SIG) && + strncmp (map, PEM_SIG, sizeof (PEM_SIG) - 1) == 0) { + key_format = RSPAMD_DKIM_KEY_PEM; + } else if (is_valid_base64 ((uint8_t *)map, maplen)) { + key_format = RSPAMD_DKIM_KEY_BASE64; + } else { + key_format = RSPAMD_DKIM_KEY_RAW; + } + } + if (key_format == RSPAMD_DKIM_KEY_UNKNOWN) { + if (keylen > sizeof (PEM_SIG) && + strncmp (key, PEM_SIG, sizeof (PEM_SIG) - 1) == 0) { + key_format = RSPAMD_DKIM_KEY_PEM; + } else { + key_format = RSPAMD_DKIM_KEY_RAW; + } + } + if (key_format == RSPAMD_DKIM_KEY_BASE64) { + key_format = RSPAMD_DKIM_KEY_RAW; + tmp = g_malloc (keylen); + rspamd_cryptobox_base64_decode (key, keylen, tmp, &keylen); + key = tmp; + } + + 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 { + rspamd_lru_hash_insert (dkim_module_ctx->dkim_sign_hash, + g_strdup (hex_hash), ret, time (NULL), 0); + } + + if (map != NULL) + munmap (map, maplen); + if (tmp != NULL) { + rspamd_explicit_memzero (tmp, keylen); + g_free (tmp); + } return ret; } +#undef PEM_SIG static gint lua_dkim_sign_handler (lua_State *L) @@ -718,86 +833,24 @@ lua_dkim_sign_handler (lua_State *L) (GDestroyNotify)rspamd_dkim_sign_key_unref); } -#define PEM_SIG "-----BEGIN" if (key) { - if (key[0] == '.' || key[0] == '/') { - /* Likely raw path */ - dkim_key = rspamd_lru_hash_lookup (dkim_module_ctx->dkim_sign_hash, - key, time (NULL)); - - if (dkim_key == NULL) { - dkim_key = rspamd_dkim_sign_key_load (key, strlen (key), - RSPAMD_DKIM_SIGN_KEY_FILE, &err); - - if (dkim_key == NULL) { - msg_err_task ("cannot load dkim key %s: %e", - key, err); - g_error_free (err); - - lua_pushboolean (L, FALSE); - return 1; - } - - rspamd_lru_hash_insert (dkim_module_ctx->dkim_sign_hash, - g_strdup (key), dkim_key, - time (NULL), 0); - } - } - else if (keylen > sizeof (PEM_SIG) && - strncmp (key, PEM_SIG, sizeof (PEM_SIG) - 1) == 0) { - /* Pem header found */ - dkim_key = dkim_module_load_key_format (L, task, key, keylen, - RSPAMD_DKIM_SIGN_KEY_PEM); - - if (dkim_key == NULL) { - lua_pushboolean (L, FALSE); - return 1; - } - } - else { - dkim_key = dkim_module_load_key_format (L, task, key, keylen, - RSPAMD_DKIM_SIGN_KEY_BASE64); - - if (dkim_key == NULL) { - lua_pushboolean (L, FALSE); - return 1; - } - } - } - else if (rawkey) { - key = rawkey; - keylen = rawlen; - - if (keylen > sizeof (PEM_SIG) && - strncmp (key, PEM_SIG, sizeof (PEM_SIG) - 1) == 0) { - /* Pem header found */ - dkim_key = dkim_module_load_key_format (L, task, key, keylen, - RSPAMD_DKIM_SIGN_KEY_PEM); - - if (dkim_key == NULL) { - lua_pushboolean (L, FALSE); - return 1; - } - } - else { - dkim_key = dkim_module_load_key_format (L, task, key, keylen, - RSPAMD_DKIM_SIGN_KEY_BASE64); - - if (dkim_key == NULL) { - lua_pushboolean (L, FALSE); - return 1; - } - } - } - else { + 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; } -#undef PEM_SIG + if (dkim_key == NULL) { + lua_pushboolean (L, FALSE); + return 1; + } if (sign_type_str) { if (strcmp (sign_type_str, "dkim") == 0) { @@ -1311,226 +1364,167 @@ dkim_sign_callback (struct rspamd_task *task, GString *tb, *hdr; GError *err = NULL; const gchar *selector = NULL, *domain = NULL, *key = NULL, *key_type = NULL, - *sign_type_str = NULL, *lru_key, *arc_cv = NULL; + *sign_type_str = NULL, *arc_cv = NULL; rspamd_dkim_sign_context_t *ctx; - rspamd_dkim_sign_key_t *dkim_key; - enum rspamd_dkim_sign_key_type key_sign_type = RSPAMD_DKIM_SIGN_KEY_FILE; + rspamd_dkim_key_t *dkim_key; + enum rspamd_dkim_key_format key_format = RSPAMD_DKIM_KEY_FILE; enum rspamd_dkim_type sign_type = RSPAMD_DKIM_NORMAL; - guchar h[rspamd_cryptobox_HASHBYTES], - hex_hash[rspamd_cryptobox_HASHBYTES * 2 + 1]; struct dkim_ctx *dkim_module_ctx = dkim_get_context (task->cfg); - if (dkim_module_ctx->sign_condition_ref != -1) { - sign = FALSE; - L = task->cfg->lua_state; - lua_pushcfunction (L, &rspamd_lua_traceback); - err_idx = lua_gettop (L); - - lua_rawgeti (L, LUA_REGISTRYINDEX, dkim_module_ctx->sign_condition_ref); - ptask = lua_newuserdata (L, sizeof (struct rspamd_task *)); - *ptask = task; - rspamd_lua_setclass (L, "rspamd{task}", -1); - - if (lua_pcall (L, 1, 1, err_idx) != 0) { - tb = lua_touserdata (L, -1); - msg_err_task ("call to user extraction script failed: %v", tb); - g_string_free (tb, TRUE); - } - else { - if (lua_istable (L, -1)) { - /* - * Get the following elements: - * - selector - * - domain - * - key - */ - if (!rspamd_lua_parse_table_arguments (L, -1, &err, - "*key=V;*domain=S;*selector=S;type=S;key_type=S;" - "sign_type=S;arc_cv=S;arc_idx=I", - &len, &key, &domain, &selector, - &key_type, &key_type, &sign_type_str, &arc_cv, - &arc_idx)) { - msg_err_task ("invalid return value from sign condition: %e", - err); - g_error_free (err); - rspamd_symcache_finalize_item (task, item); + if (dkim_module_ctx->sign_condition_ref == -1) { + rspamd_symcache_finalize_item (task, item); + return; + } - return; - } + sign = FALSE; + L = task->cfg->lua_state; + lua_pushcfunction (L, &rspamd_lua_traceback); + err_idx = lua_gettop (L); - if (key_type) { - if (strcmp (key_type, "file") == 0) { - key_sign_type = RSPAMD_DKIM_SIGN_KEY_FILE; - } - else if (strcmp (key_type, "base64") == 0) { - key_sign_type = RSPAMD_DKIM_SIGN_KEY_BASE64; - } - else if (strcmp (key_type, "pem") == 0) { - key_sign_type = RSPAMD_DKIM_SIGN_KEY_PEM; - } - else if (strcmp (key_type, "der") == 0 || - strcmp (key_type, "raw") == 0) { - key_sign_type = RSPAMD_DKIM_SIGN_KEY_DER; - } - else { - lua_settop (L, 0); - luaL_error (L, "unknown key type: %s", - key_type); - rspamd_symcache_finalize_item (task, item); + lua_rawgeti (L, LUA_REGISTRYINDEX, dkim_module_ctx->sign_condition_ref); + ptask = lua_newuserdata (L, sizeof (struct rspamd_task *)); + *ptask = task; + rspamd_lua_setclass (L, "rspamd{task}", -1); - return; - } - } + if (lua_pcall (L, 1, 1, err_idx) != 0) { + tb = lua_touserdata (L, -1); + msg_err_task ("call to user extraction script failed: %v", tb); + g_string_free (tb, TRUE); + } + else { + if (lua_istable (L, -1)) { + /* + * Get the following elements: + * - selector + * - domain + * - key + */ + if (!rspamd_lua_parse_table_arguments (L, -1, &err, + "*key=V;*domain=S;*selector=S;type=S;key_type=S;" + "sign_type=S;arc_cv=S;arc_idx=I", + &len, &key, &domain, &selector, + &key_type, &key_type, &sign_type_str, &arc_cv, + &arc_idx)) { + msg_err_task ("invalid return value from sign condition: %e", + err); + g_error_free (err); + rspamd_symcache_finalize_item (task, item); - if (sign_type_str) { - if (strcmp (sign_type_str, "dkim") == 0) { - sign_type = RSPAMD_DKIM_NORMAL; - } - else if (strcmp (sign_type_str, "arc-sign") == 0) { - sign_type = RSPAMD_DKIM_ARC_SIG; - if (arc_idx == 0) { - lua_settop (L, 0); - luaL_error (L, "no arc idx specified"); - rspamd_symcache_finalize_item (task, item); - - return; - } - } - else if (strcmp (sign_type_str, "arc-seal") == 0) { - sign_type = RSPAMD_DKIM_ARC_SEAL; - if (arc_cv == NULL) { - lua_settop (L, 0); - luaL_error (L, "no arc cv specified"); - rspamd_symcache_finalize_item (task, item); - - return; - } - if (arc_idx == 0) { - lua_settop (L, 0); - luaL_error (L, "no arc idx specified"); - rspamd_symcache_finalize_item (task, item); - - return; - } - } - else { - lua_settop (L, 0); - luaL_error (L, "unknown sign type: %s", - sign_type_str); - rspamd_symcache_finalize_item (task, item); + return; + } - return; - } + if (key_type) { + if (strcmp (key_type, "file") == 0) { + key_format = RSPAMD_DKIM_KEY_FILE; } - - if (key_sign_type == RSPAMD_DKIM_SIGN_KEY_FILE) { - - dkim_key = rspamd_lru_hash_lookup ( - dkim_module_ctx->dkim_sign_hash, - key, time (NULL)); - lru_key = key; + else if (strcmp (key_type, "base64") == 0) { + key_format = RSPAMD_DKIM_KEY_BASE64; } - else { - /* Prehash */ - memset (hex_hash, 0, sizeof (hex_hash)); - rspamd_cryptobox_hash (h, key, len, NULL, 0); - rspamd_encode_hex_buf (h, sizeof (h), - hex_hash, sizeof (hex_hash)); - dkim_key = rspamd_lru_hash_lookup ( - dkim_module_ctx->dkim_sign_hash, - hex_hash, time (NULL)); - lru_key = hex_hash; + else if (strcmp (key_type, "pem") == 0) { + key_format = RSPAMD_DKIM_KEY_PEM; } + else if (strcmp (key_type, "der") == 0 || + strcmp (key_type, "raw") == 0) { + key_format = RSPAMD_DKIM_KEY_RAW; + } + else { + lua_settop (L, 0); + luaL_error (L, "unknown key type: %s", + key_type); + rspamd_symcache_finalize_item (task, item); - if (dkim_key == NULL) { - dkim_key = rspamd_dkim_sign_key_load (key, len, - key_sign_type, &err); + return; + } + } - if (dkim_key == NULL) { - msg_err_task ("cannot load dkim key %s: %e", - lru_key, err); - g_error_free (err); + if (sign_type_str) { + if (strcmp (sign_type_str, "dkim") == 0) { + sign_type = RSPAMD_DKIM_NORMAL; + } + else if (strcmp (sign_type_str, "arc-sign") == 0) { + sign_type = RSPAMD_DKIM_ARC_SIG; + if (arc_idx == 0) { + lua_settop (L, 0); + luaL_error (L, "no arc idx specified"); rspamd_symcache_finalize_item (task, item); return; } - else { - rspamd_lru_hash_insert (dkim_module_ctx->dkim_sign_hash, - g_strdup (lru_key), dkim_key, - time (NULL), 0); - } } - else if (rspamd_dkim_sign_key_maybe_invalidate (dkim_key, - key_sign_type, key, len)) { - /* - * Invalidate and reload DKIM key, - * removal from lru cache also cleanup the key and value - */ - - rspamd_lru_hash_remove (dkim_module_ctx->dkim_sign_hash, - lru_key); - dkim_key = rspamd_dkim_sign_key_load (key, len, - key_sign_type, &err); - - if (dkim_key == NULL) { - msg_err_task ("cannot load dkim key %s: %e", - lru_key, err); - g_error_free (err); + else if (strcmp (sign_type_str, "arc-seal") == 0) { + sign_type = RSPAMD_DKIM_ARC_SEAL; + if (arc_cv == NULL) { + lua_settop (L, 0); + luaL_error (L, "no arc cv specified"); rspamd_symcache_finalize_item (task, item); return; } - else { - rspamd_lru_hash_insert (dkim_module_ctx->dkim_sign_hash, - g_strdup (lru_key), dkim_key, - time (NULL), 0); + if (arc_idx == 0) { + lua_settop (L, 0); + luaL_error (L, "no arc idx specified"); + rspamd_symcache_finalize_item (task, item); + + return; } } - - ctx = rspamd_create_dkim_sign_context (task, dkim_key, - DKIM_CANON_RELAXED, DKIM_CANON_RELAXED, - dkim_module_ctx->sign_headers, - sign_type, - &err); - - if (ctx == NULL) { - msg_err_task ("cannot create sign context: %e", - err); - g_error_free (err); + else { + lua_settop (L, 0); + luaL_error (L, "unknown sign type: %s", + sign_type_str); rspamd_symcache_finalize_item (task, item); return; } + } - hdr = rspamd_dkim_sign (task, selector, domain, 0, 0, - arc_idx, arc_cv, - ctx); - - if (hdr) { - rspamd_mempool_set_variable (task->task_pool, - "dkim-signature", - hdr, rspamd_gstring_free_hard); - } + dkim_key = dkim_module_load_key_format (task, dkim_module_ctx, + key, len, key_format); - sign = TRUE; + if (dkim_key == NULL) { + rspamd_symcache_finalize_item (task, item); + return; } - else { - sign = FALSE; + + ctx = rspamd_create_dkim_sign_context (task, dkim_key, + DKIM_CANON_RELAXED, DKIM_CANON_RELAXED, + dkim_module_ctx->sign_headers, + sign_type, + &err); + + if (ctx == NULL) { + msg_err_task ("cannot create sign context: %e", + err); + g_error_free (err); + rspamd_symcache_finalize_item (task, item); + + return; } - } - /* Result + error function */ - lua_settop (L, 0); + hdr = rspamd_dkim_sign (task, selector, domain, 0, 0, + arc_idx, arc_cv, + ctx); - if (!sign) { - msg_debug_task ("skip signing as dkim condition callback returned" - " false"); - rspamd_symcache_finalize_item (task, item); + if (hdr) { + rspamd_mempool_set_variable (task->task_pool, + "dkim-signature", + hdr, rspamd_gstring_free_hard); + } - return; + sign = TRUE; + } + else { + sign = FALSE; } } + /* Result + error function */ + lua_settop (L, 0); + + if (!sign) { + msg_debug_task ("skip signing as dkim condition callback returned" + " false"); + } rspamd_symcache_finalize_item (task, item); } |