From fcd2daa1c3d0613f6adb9bbb3f7cccac8318cf50 Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Thu, 7 Jul 2016 15:04:32 +0100 Subject: [PATCH] [Rework] Fix DKIM headers canonicalization --- src/libserver/dkim.c | 299 +++++++++++-------------------------------- 1 file changed, 73 insertions(+), 226 deletions(-) diff --git a/src/libserver/dkim.c b/src/libserver/dkim.c index 4d6fc6940..3fdaae84d 100644 --- a/src/libserver/dkim.c +++ b/src/libserver/dkim.c @@ -1660,260 +1660,110 @@ rspamd_dkim_canonize_header_relaxed (struct rspamd_dkim_common_ctx *ctx, return TRUE; } -struct rspamd_dkim_sign_chunk { - const gchar *begin; - gsize len; - gboolean append_crlf; -}; static gboolean -rspamd_dkim_canonize_header_simple (struct rspamd_dkim_common_ctx *ctx, - const gchar *headers, - gsize headers_length, +rspamd_dkim_canonize_header (struct rspamd_dkim_common_ctx *ctx, + struct rspamd_task *task, const gchar *header_name, guint count, - gboolean is_sign, + const gchar *dkim_header, const gchar *dkim_domain) { - const gchar *p, *c, *end; - gint state = 0, hlen; - gboolean found = FALSE; - GArray *to_sign; - struct rspamd_dkim_sign_chunk chunk, *elt = NULL; - gint i; - - /* This process is very similar to raw headers processing */ - to_sign = - g_array_sized_new (FALSE, - FALSE, - sizeof (struct rspamd_dkim_sign_chunk), - count); - p = headers; - end = p + headers_length; - c = p; - hlen = strlen (header_name); + struct raw_header *rh, *rh_iter; + guint rh_num = 0; + guint i; + GPtrArray *sign_headers; - while (p < end) { - switch (state) { - case 0: - /* Compare state */ - if (*p == ':') { - /* Compare header's name with desired one */ - if (p - c == hlen) { - if (g_ascii_strncasecmp (c, header_name, hlen) == 0) { - /* Get value */ - state = 2; - } - else { - /* Skip the whole header */ - state = 1; - } - } - else { - /* Skip the whole header */ - state = 1; - } + if (dkim_header == NULL) { + rh = g_hash_table_lookup (task->raw_headers, header_name); + + if (rh) { + LL_FOREACH (rh, rh_iter) { + rh_num++; } - p++; - break; - case 1: - /* Skip header state */ - if (*p == '\n' && !g_ascii_isspace (p[1])) { - /* Header is skipped */ - state = 0; - c = p + 1; + + if (rh_num > count) { + /* Set skip count */ + rh_num -= count; } - p++; - break; - case 2: - /* c contains the beginning of header */ - if (*p == '\n' && (!g_ascii_isspace (p[1]) || p[1] == '\0')) { - chunk.begin = c; - if (*(p - 1) == '\r') { - chunk.len = p - c + 1; - chunk.append_crlf = FALSE; - } - else { - /* Need append CRLF as linefeed is not proper */ - chunk.len = p - c; - chunk.append_crlf = TRUE; - } - g_array_append_val (to_sign, chunk); - c = p + 1; - state = 0; - found = TRUE; + else { + rh_num = 0; } - p++; - break; - } - } - if (found) { - if (!is_sign) { + sign_headers = g_ptr_array_sized_new (rh_num); + /* Skip number of headers */ + rh_iter = rh; + while (rh_num) { + rh_iter = rh_iter->next; + rh_num--; + } + + /* Now insert required headers */ + while (rh_iter) { + g_ptr_array_add (sign_headers, rh_iter); + rh_iter = rh_iter->next; + } - for (i = to_sign->len - 1; i >= 0 && count > 0; i--, count--) { - elt = - &g_array_index (to_sign, struct rspamd_dkim_sign_chunk, i); + for (i = 0; i < sign_headers->len; i ++) { + rh = g_ptr_array_index (sign_headers, i); - if (!chunk.append_crlf) { + if (ctx->header_canon_type == DKIM_CANON_SIMPLE) { + rspamd_dkim_hash_update (ctx->headers_hash, rh->raw_value, + rh->raw_len); msg_debug_dkim ("update signature with header: %*s", - elt->len, - elt->begin); - rspamd_dkim_hash_update (ctx->headers_hash, - elt->begin, - elt->len); + (gint)rh->raw_len, rh->raw_value); } else { - msg_debug_dkim ("update signature with header: %*s", - elt->len + 1, - elt->begin); - rspamd_dkim_hash_update (ctx->headers_hash, - elt->begin, - elt->len + 1); - } - } - } - else { - /* Try to find the proper header by domain */ - for (i = to_sign->len - 1; i >= 0; i--) { - elt = &g_array_index (to_sign, - struct rspamd_dkim_sign_chunk, - i); - if (rspamd_substring_search (elt->begin, elt->len, - dkim_domain, strlen (dkim_domain)) != -1) { - break; + if (!rspamd_dkim_canonize_header_relaxed (ctx, rh->value, + header_name, FALSE)) { + + g_ptr_array_free (sign_headers, TRUE); + return FALSE; + } } } - if (elt && elt->append_crlf) { - rspamd_dkim_signature_update (ctx, elt->begin, elt->len + 1); - } - else if (elt) { - rspamd_dkim_signature_update (ctx, elt->begin, elt->len); - } + g_ptr_array_free (sign_headers, TRUE); } } - - g_array_free (to_sign, TRUE); - - return found; -} - -static gboolean -rspamd_dkim_canonize_header (struct rspamd_dkim_common_ctx *ctx, - struct rspamd_task *task, - const gchar *header_name, - guint count, - const gchar *dkim_header, - const gchar *dkim_domain) -{ - struct raw_header *rh, *rh_iter; - guint rh_num = 0; - GList *nh = NULL, *cur; - - if (ctx->header_canon_type == DKIM_CANON_SIMPLE) { - return rspamd_dkim_canonize_header_simple (ctx, - task->raw_headers_content.begin, - task->raw_headers_content.len, - header_name, - count, - dkim_header != NULL, - dkim_domain); - } else { - rh = g_hash_table_lookup (task->raw_headers, header_name); - if (rh) { - if (dkim_header == NULL) { - rh_iter = rh; - while (rh_iter) { - rh_num++; - rh_iter = rh_iter->next; + /* For signature check just use the saved dkim header */ + if (ctx->header_canon_type == DKIM_CANON_SIMPLE) { + /* We need to find our own signature and use it */ + rh = g_hash_table_lookup (task->raw_headers, DKIM_SIGNHEADER); + + if (rh) { + /* We need to find our own signature */ + if (!dkim_domain) { + return FALSE; } - if (rh_num > count) { - /* Set skip count */ - rh_num -= count; - } - else { - rh_num = 0; - } - rh_iter = rh; - while (rh_num) { - rh_iter = rh_iter->next; - rh_num--; - } - /* Now insert required headers */ - while (rh_iter) { - nh = g_list_prepend (nh, rh_iter); - rh_iter = rh_iter->next; - } - cur = nh; - while (cur) { - rh = cur->data; - if (!rspamd_dkim_canonize_header_relaxed (ctx, rh->value, - header_name, FALSE)) { - g_list_free (nh); - return FALSE; + LL_FOREACH (rh, rh_iter) { + if (rspamd_substring_search_twoway (rh->raw_value, + rh->raw_len, dkim_domain, + strlen (dkim_domain)) != -1) { + rspamd_dkim_signature_update (ctx, rh->raw_value, + rh->raw_len); + break; } - cur = g_list_next (cur); - } - if (nh != NULL) { - g_list_free (nh); } } else { - /* For signature check just use the saved dkim header */ - rspamd_dkim_canonize_header_relaxed (ctx, + return FALSE; + } + } + else { + if (!rspamd_dkim_canonize_header_relaxed (ctx, dkim_header, header_name, - TRUE); + TRUE)) { + return FALSE; } - - return TRUE; } } - /* TODO: Implement relaxed algorithm */ - return FALSE; -} - -static inline const gchar * -rspamd_dkim_body_start (const gchar *headers_end, const gchar *body_end) -{ - const gchar *p = headers_end; - enum { - st_start = 0, - st_cr, - st_lf, - } state = st_start; - - if (headers_end + 1 >= body_end) { - return headers_end; - } - - switch (state) { - case st_start: - if (*p == '\r') { - p ++; - state = st_cr; - } - else if (*p == '\n') { - p ++; - state = st_lf; - } - break; - case st_cr: - if (*p == '\n' && p < body_end) { - /* CRLF */ - p ++; - } - break; - case st_lf: - break; - } - - return p; + return TRUE; } /** @@ -1928,7 +1778,7 @@ rspamd_dkim_check (rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key, struct rspamd_task *task) { - const gchar *p, *headers_end = NULL, *body_end, *body_start; + const gchar *p, *body_end, *body_start; guchar raw_digest[EVP_MAX_MD_SIZE]; gsize dlen; gint res = DKIM_CONTINUE; @@ -1943,9 +1793,7 @@ rspamd_dkim_check (rspamd_dkim_context_t *ctx, /* First of all find place of body */ p = task->msg.begin; body_end = task->msg.begin + task->msg.len; - headers_end = task->msg.begin + task->raw_headers_content.len; - - body_start = rspamd_dkim_body_start (headers_end, body_end); + body_start = task->msg.begin + task->raw_headers_content.len; /* Start canonization of body part */ if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end)) { @@ -2188,7 +2036,7 @@ rspamd_dkim_sign (struct rspamd_task *task, { GString *hdr; struct rspamd_dkim_header *dh; - const gchar *p, *headers_end = NULL, *body_end, *body_start; + const gchar *p, *body_end, *body_start; guchar raw_digest[EVP_MAX_MD_SIZE]; gsize dlen; guint i, j; @@ -2201,8 +2049,7 @@ rspamd_dkim_sign (struct rspamd_task *task, /* First of all find place of body */ p = task->msg.begin; body_end = task->msg.begin + task->msg.len; - headers_end = task->msg.begin + task->raw_headers_content.len; - body_start = rspamd_dkim_body_start (headers_end, body_end); + body_start = task->msg.begin + task->raw_headers_content.len; if (len > 0) { ctx->common.len = len; -- 2.39.5