]> source.dussan.org Git - rspamd.git/commitdiff
[Feature] Check dkim sign keys for modifications
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 20 May 2017 08:48:40 +0000 (09:48 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Sat, 20 May 2017 08:48:40 +0000 (09:48 +0100)
src/libserver/dkim.c
src/libserver/dkim.h
src/plugins/dkim_check.c

index 3a314cac9e1dd0ce1414bc67e8277547469d6f4f..bb1113d005d47b487bf71b2cf0f29ee89ced8f3c 100644 (file)
 #include "dkim.h"
 #include "dns.h"
 #include "utlist.h"
+#include "unix-std.h"
 
 #include <openssl/evp.h>
 #include <openssl/rsa.h>
 #include <openssl/engine.h>
-#include <sys/mman.h>
 
 /* special DNS tokens */
 #define DKIM_DNSKEYNAME     "_domainkey"
@@ -127,6 +127,7 @@ struct rspamd_dkim_sign_key_s {
        RSA *key_rsa;
        BIO *key_bio;
        EVP_PKEY *key_evp;
+       time_t mtime;
        ref_entry_t ref;
 };
 
@@ -1968,11 +1969,23 @@ rspamd_dkim_sign_key_load (const gchar *what, gsize len,
        gpointer map;
        gsize map_len = 0;
        rspamd_dkim_sign_key_t *nkey;
+       struct stat st;
+       time_t mtime = 0;
 
        if (type == RSPAMD_DKIM_SIGN_KEY_FILE) {
                gchar fpath[PATH_MAX];
 
                rspamd_snprintf (fpath, sizeof (fpath), "%*s", (gint)len, what);
+
+               if (stat (fpath, &st) == -1) {
+                       g_set_error (err, dkim_error_quark (), DKIM_SIGERROR_KEYFAIL,
+                                       "cannot stat private key %s: %s",
+                                       fpath, strerror (errno));
+
+                       return NULL;
+               }
+
+               mtime = st.st_mtime;
                map = rspamd_file_xmap (fpath, PROT_READ, &map_len, TRUE);
 
                if (map == NULL) {
@@ -1986,6 +1999,7 @@ rspamd_dkim_sign_key_load (const gchar *what, gsize len,
 
        nkey = g_slice_alloc0 (sizeof (*nkey));
        nkey->type = type;
+       nkey->mtime = mtime;
 
        switch (type) {
        case RSPAMD_DKIM_SIGN_KEY_FILE:
@@ -2064,6 +2078,31 @@ rspamd_dkim_sign_key_load (const gchar *what, gsize len,
        return nkey;
 }
 
+gboolean
+rspamd_dkim_sign_key_maybe_invalidate (rspamd_dkim_sign_key_t *key,
+               enum rspamd_dkim_sign_key_type type,
+               const gchar *what, gsize len)
+{
+       struct stat st;
+
+       if (type == RSPAMD_DKIM_SIGN_KEY_FILE) {
+               gchar fpath[PATH_MAX];
+
+               rspamd_snprintf (fpath, sizeof (fpath), "%*s", (gint) len, what);
+
+               if (stat (fpath, &st) == -1) {
+                       /* Prefer to use cached key since it is absent on FS */
+                       return FALSE;
+               }
+
+               if (st.st_mtime > key->mtime) {
+                       return TRUE;
+               }
+       }
+
+       return FALSE;
+}
+
 rspamd_dkim_sign_context_t *
 rspamd_create_dkim_sign_context (struct rspamd_task *task,
                rspamd_dkim_sign_key_t *priv_key,
index a2bd4d0e20b632b518d3686b31b530aa4685c4e0..7e025a4c05c39da41c29920ed4637efaa31279f4 100644 (file)
@@ -150,6 +150,15 @@ rspamd_dkim_sign_key_t* rspamd_dkim_sign_key_load (const gchar *what, gsize len,
                enum rspamd_dkim_sign_key_type type,
                GError **err);
 
+/**
+ * Invalidate modified sign key
+ * @param key
+ * @return
+ */
+gboolean rspamd_dkim_sign_key_maybe_invalidate (rspamd_dkim_sign_key_t *key,
+               enum rspamd_dkim_sign_key_type type,
+               const gchar *what, gsize len);
+
 /**
  * Make DNS request for specified context and obtain and parse key
  * @param ctx dkim context from signature
index e831373398104d06ced60265ef6ba4f441b2e854..2a1706c3d9fc41a9d0de6bf52cd444c60539f5ae 100644 (file)
@@ -1117,7 +1117,31 @@ dkim_sign_callback (struct rspamd_task *task, void *unused)
 
                                        if (dkim_key == NULL) {
                                                msg_err_task ("cannot load dkim key %s: %e",
-                                                               key, err);
+                                                               lru_key, err);
+                                               g_error_free (err);
+
+                                               return;
+                                       }
+
+                                       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,
+                                               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,
+                                                       sign_type, &err);
+
+                                       if (dkim_key == NULL) {
+                                               msg_err_task ("cannot load dkim key %s: %e",
+                                                               lru_key, err);
                                                g_error_free (err);
 
                                                return;