Browse Source

[Feature] Implement caching for dkim body hashes

tags/1.6.0
Vsevolod Stakhov 7 years ago
parent
commit
760168fc69
2 changed files with 131 additions and 40 deletions
  1. 130
    40
      src/libserver/dkim.c
  2. 1
    0
      src/libserver/mempool_vars_internal.h

+ 130
- 40
src/libserver/dkim.c View File

@@ -20,6 +20,7 @@
#include "dns.h"
#include "utlist.h"
#include "unix-std.h"
#include "mempool_vars_internal.h"

#include <openssl/evp.h>
#include <openssl/rsa.h>
@@ -2003,6 +2004,40 @@ rspamd_dkim_canonize_header (struct rspamd_dkim_common_ctx *ctx,
return TRUE;
}

struct rspamd_dkim_cached_hash {
guchar *digest_normal;
guchar *digest_cr;
guchar *digest_crlf;
gchar *type;
};

static struct rspamd_dkim_cached_hash *
rspamd_dkim_check_bh_cached (struct rspamd_dkim_common_ctx *ctx,
struct rspamd_task *task, gsize bhlen, gboolean is_sign)
{
gchar typebuf[64];
struct rspamd_dkim_cached_hash *res;

rspamd_snprintf (typebuf, sizeof (typebuf),
RSPAMD_MEMPOOL_DKIM_BH_CACHE "%z_%s_%d_%z",
bhlen,
ctx->body_canon_type == DKIM_CANON_RELAXED ? "1" : "0",
!!is_sign,
ctx->len);

res = rspamd_mempool_get_variable (task->task_pool,
typebuf);

if (!res) {
res = rspamd_mempool_alloc0 (task->task_pool, sizeof (*res));
res->type = rspamd_mempool_strdup (task->task_pool, typebuf);
rspamd_mempool_set_variable (task->task_pool,
res->type, res, NULL);
}

return res;
}

/**
* Check task for dkim context using dkim key
* @param ctx dkim verify context
@@ -2017,8 +2052,9 @@ rspamd_dkim_check (rspamd_dkim_context_t *ctx,
{
const gchar *body_end, *body_start;
guchar raw_digest[EVP_MAX_MD_SIZE];
EVP_MD_CTX *cpy_ctx;
gsize dlen;
struct rspamd_dkim_cached_hash *cached_bh = NULL;
EVP_MD_CTX *cpy_ctx = NULL;
gsize dlen = 0;
enum rspamd_dkim_check_result res = DKIM_CONTINUE;
guint i;
struct rspamd_dkim_header *dh;
@@ -2037,10 +2073,16 @@ rspamd_dkim_check (rspamd_dkim_context_t *ctx,
}

if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
/* Start canonization of body part */
if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end,
FALSE)) {
return DKIM_RECORD_ERROR;
dlen = EVP_MD_CTX_size (ctx->common.body_hash);
cached_bh = rspamd_dkim_check_bh_cached (&ctx->common, task,
dlen, FALSE);

if (!cached_bh->digest_normal) {
/* Start canonization of body part */
if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end,
FALSE)) {
return DKIM_RECORD_ERROR;
}
}
}

@@ -2069,54 +2111,89 @@ rspamd_dkim_check (rspamd_dkim_context_t *ctx,


if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
dlen = EVP_MD_CTX_size (ctx->common.body_hash);
/* Copy md_ctx to deal with broken CRLF at the end */
cpy_ctx = EVP_MD_CTX_create ();
EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);

/* Check bh field */
if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
msg_debug_dkim ("bh value mismatch: %*xs versus %*xs, try add CRLF",
dlen, ctx->bh,
dlen, raw_digest);
/* Try add CRLF */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
EVP_MD_CTX_cleanup (cpy_ctx);
#else
EVP_MD_CTX_reset (cpy_ctx);
#endif
if (!cached_bh->digest_normal) {
/* Copy md_ctx to deal with broken CRLF at the end */
cpy_ctx = EVP_MD_CTX_create ();
EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
EVP_DigestUpdate (cpy_ctx, "\r\n", 2);
EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);

if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
cached_bh->digest_normal = rspamd_mempool_alloc (task->task_pool,
sizeof (raw_digest));
memcpy (cached_bh->digest_normal, raw_digest, sizeof (raw_digest));
}

/* Check bh field */
if (memcmp (ctx->bh, cached_bh->digest_normal, ctx->bhlen) != 0) {
if (cpy_ctx) {
msg_debug_dkim (
"bh value mismatch: %*xs versus %*xs, try add LF",
"bh value mismatch: %*xs versus %*xs, try add CRLF",
dlen, ctx->bh,
dlen, raw_digest);

/* Try add LF */
dlen, cached_bh->digest_normal);
/* Try add CRLF */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
EVP_MD_CTX_cleanup (cpy_ctx);
#else
EVP_MD_CTX_reset (cpy_ctx);
#endif
EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
EVP_DigestUpdate (cpy_ctx, "\n", 1);
EVP_DigestUpdate (cpy_ctx, "\r\n", 2);
EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);
cached_bh->digest_crlf = rspamd_mempool_alloc (task->task_pool,
sizeof (raw_digest));
memcpy (cached_bh->digest_crlf, raw_digest, sizeof (raw_digest));

if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
msg_debug_dkim ("bh value mismatch: %*xs versus %*xs",
msg_debug_dkim (
"bh value mismatch: %*xs versus %*xs, try add LF",
dlen, ctx->bh,
dlen, raw_digest);

/* Try add LF */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
EVP_MD_CTX_cleanup (cpy_ctx);
#else
EVP_MD_CTX_reset (cpy_ctx);
#endif
EVP_MD_CTX_destroy (cpy_ctx);
return DKIM_REJECT;
EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
EVP_DigestUpdate (cpy_ctx, "\n", 1);
EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);
cached_bh->digest_cr = rspamd_mempool_alloc (task->task_pool,
sizeof (raw_digest));
memcpy (cached_bh->digest_cr, raw_digest, sizeof (raw_digest));

if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
msg_debug_dkim ("bh value mismatch: %*xs versus %*xs",
dlen, ctx->bh,
dlen, raw_digest);
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
EVP_MD_CTX_cleanup (cpy_ctx);
#else
EVP_MD_CTX_reset (cpy_ctx);
#endif
EVP_MD_CTX_destroy (cpy_ctx);
return DKIM_REJECT;
}
}
}
else if (cached_bh->digest_crlf) {
if (memcmp (ctx->bh, cached_bh->digest_crlf, ctx->bhlen) != 0) {
msg_debug_dkim ("bh value mismatch: %*xs versus %*xs",
dlen, ctx->bh,
dlen, cached_bh->digest_crlf);

if (cached_bh->digest_cr) {
if (memcmp (ctx->bh, cached_bh->digest_cr, ctx->bhlen) != 0) {
msg_debug_dkim (
"bh value mismatch: %*xs versus %*xs",
dlen, ctx->bh,
dlen, cached_bh->digest_cr);

return DKIM_REJECT;
}
}
else {
return DKIM_REJECT;
}
}
}
}
@@ -2459,7 +2536,8 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
struct rspamd_dkim_header *dh;
const gchar *body_end, *body_start, *hname;
guchar raw_digest[EVP_MAX_MD_SIZE];
gsize dlen;
struct rspamd_dkim_cached_hash *cached_bh = NULL;
gsize dlen = 0;
guint i, j;
gchar *b64_data;
guchar *rsa_buf;
@@ -2481,9 +2559,16 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,

/* Start canonization of body part */
if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end,
TRUE)) {
return NULL;
dlen = EVP_MD_CTX_size (ctx->common.body_hash);
cached_bh = rspamd_dkim_check_bh_cached (&ctx->common, task,
dlen, TRUE);

if (!cached_bh->digest_normal) {
/* Start canonization of body part */
if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end,
TRUE)) {
return NULL;
}
}
}

@@ -2544,10 +2629,15 @@ rspamd_dkim_sign (struct rspamd_task *task, const gchar *selector,
hdr->str[hdr->len - 1] = ';';

if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
dlen = EVP_MD_CTX_size (ctx->common.body_hash);
EVP_DigestFinal_ex (ctx->common.body_hash, raw_digest, NULL);
if (!cached_bh->digest_normal) {
EVP_DigestFinal_ex (ctx->common.body_hash, raw_digest, NULL);
cached_bh->digest_normal = rspamd_mempool_alloc (task->task_pool,
sizeof (raw_digest));
memcpy (cached_bh->digest_normal, raw_digest, sizeof (raw_digest));
}


b64_data = rspamd_encode_base64 (raw_digest, dlen, 0, NULL);
b64_data = rspamd_encode_base64 (cached_bh->digest_normal, dlen, 0, NULL);
rspamd_printf_gstring (hdr, " bh=%s; b=", b64_data);
g_free (b64_data);
}

+ 1
- 0
src/libserver/mempool_vars_internal.h View File

@@ -30,5 +30,6 @@
#define RSPAMD_MEMPOOL_RMILTER_REPLY "rmilter_reply"
#define RSPAMD_MEMPOOL_DKIM_SIGNATURE "dkim-signature"
#define RSPAMD_MEMPOOL_DMARC_CHECKS "dmarc_checks"
#define RSPAMD_MEMPOOL_DKIM_BH_CACHE "dkim_bh_cache"

#endif

Loading…
Cancel
Save