]> source.dussan.org Git - rspamd.git/commitdiff
[Rework] Fix DKIM headers canonicalization
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 7 Jul 2016 14:04:32 +0000 (15:04 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 7 Jul 2016 14:04:32 +0000 (15:04 +0100)
src/libserver/dkim.c

index 4d6fc69404f0cf90484b5a94292660b425d17b9a..3fdaae84ddc4db5d284f96f5439f48fbf8fc07d0 100644 (file)
@@ -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;