]> source.dussan.org Git - rspamd.git/commitdiff
Implement key extracting for dkim records.
authorVsevolod Stakhov <vsevolod@rambler-co.ru>
Fri, 25 May 2012 16:14:18 +0000 (20:14 +0400)
committerVsevolod Stakhov <vsevolod@rambler-co.ru>
Fri, 25 May 2012 16:14:18 +0000 (20:14 +0400)
src/dkim.c
src/dkim.h

index 8a25a08c2091b8774b781b4ab1682302e3da779e..97fd8db7ecb60d039aad2a2dc80c986c843c94df 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #include "main.h"
 #include "dkim.h"
+#include "dns.h"
 
 /* Parser of dkim params */
 typedef gboolean (*dkim_parse_param_f) (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err);
@@ -299,7 +300,7 @@ rspamd_dkim_parse_bodylength (rspamd_dkim_context_t* ctx, const gchar *param, gs
 rspamd_dkim_context_t*
 rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err)
 {
-       const gchar                                             *p, *c, *tag;
+       const gchar                                             *p, *c, *tag, *end;
        gsize                                                    taglen;
        gint                                                     param = DKIM_PARAM_UNKNOWN;
        time_t                                                   now;
@@ -322,7 +323,10 @@ rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err)
        state = DKIM_STATE_SKIP_SPACES;
        next_state = DKIM_STATE_TAG;
        taglen = 0;
-       while (*p) {
+       p = sig;
+       c = sig;
+       end = p + strlen (p);
+       while (p <= end) {
                switch (state) {
                case DKIM_STATE_TAG:
                        if (g_ascii_isspace (*p)) {
@@ -437,6 +441,11 @@ rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err)
                                        state = DKIM_STATE_ERROR;
                                }
                        }
+                       else if (p == end) {
+                               if (param == DKIM_PARAM_UNKNOWN || !parser_funcs[param](new, c, p - c, err)) {
+                                       state = DKIM_STATE_ERROR;
+                               }
+                       }
                        else {
                                p ++;
                        }
@@ -503,9 +512,105 @@ rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err)
                return NULL;
        }
 
+       /* Now create dns key to request further */
+       taglen = strlen (new->domain) + strlen (new->selector) + sizeof (DKIM_DNSKEYNAME) + 2;
+       new->dns_key = memory_pool_alloc (new->pool, taglen);
+       rspamd_snprintf (new->dns_key, taglen, "%s.%s.%s", new->selector, DKIM_DNSKEYNAME, new->domain);
+
        return new;
 }
 
+struct rspamd_dkim_key_cbdata {
+       rspamd_dkim_context_t *ctx;
+       dkim_key_handler_f handler;
+       gpointer ud;
+};
+
+static rspamd_dkim_key_t*
+rspamd_dkim_parse_key (const gchar *txt, gsize *keylen, GError **err)
+{
+       const gchar                                                                     *c, *p, *end;
+       gint                                                                             state = 0;
+       gsize                                                                            len;
+       rspamd_dkim_key_t                                                       *key = NULL;
+
+       c = txt;
+       p = txt;
+       end = txt + strlen (txt);
+
+       while (p <= end) {
+               switch (state) {
+               case 0:
+                       if (p != end && p[0] == 'p' && p[1] == '=') {
+                               /* We got something like public key */
+                               c = p + 2;
+                               p = c;
+                               state = 1;
+                       }
+                       else {
+                               /* Ignore everything */
+                               p ++;
+                       }
+                       break;
+               case 1:
+                       /* State when we got p= and looking for some public key */
+                       if ((*p == ';' || p == end) && p > c) {
+                               len = (p == end) ? p - c : p - c - 1;
+                               key = g_slice_alloc (len + 1);
+                               /* For free data */
+                               *keylen = len + 1;
+                               rspamd_strlcpy (key, c, len + 1);
+                               g_base64_decode_inplace (key, &len);
+                               return key;
+                       }
+                       break;
+               }
+       }
+
+       if (p - c == 0) {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_KEYREVOKED, "key was revoked");
+       }
+       else {
+               g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_KEYFAIL, "key was not found");
+       }
+
+       return NULL;
+}
+
+/* Get TXT request data and parse it */
+static void
+rspamd_dkim_dns_cb (struct rspamd_dns_reply *reply, gpointer arg)
+{
+       struct rspamd_dkim_key_cbdata                           *cbdata = arg;
+       rspamd_dkim_key_t                                                       *key;
+       GError                                                                          *err = NULL;
+       GList                                                                           *cur;
+       union rspamd_reply_element                                      *elt;
+       gsize                                                                            keylen = 0;
+
+       if (reply->code != DNS_RC_NOERROR) {
+               g_set_error (&err, DKIM_ERROR, DKIM_SIGERROR_NOKEY, "dns request to %s failed: %s", cbdata->ctx->dns_key,
+                               dns_strerror (reply->code));
+               cbdata->handler (NULL, 0, cbdata->ctx, cbdata->ud, err);
+       }
+       else {
+               cur = reply->elements;
+               while (cur) {
+                       elt = cur->data;
+                       key = rspamd_dkim_parse_key (elt->txt.data, &keylen, &err);
+                       if (key) {
+                               break;
+                       }
+               }
+               if (key != NULL && err != NULL) {
+                       /* Free error as it is insignificant */
+                       g_error_free (err);
+                       err = NULL;
+               }
+               cbdata->handler (key, keylen, cbdata->ctx, cbdata->ud, err);
+       }
+}
+
 /**
  * Make DNS request for specified context and obtain and parse key
  * @param ctx dkim context from signature
@@ -513,12 +618,21 @@ rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err)
  * @param s async session to make request
  * @return
  */
-rspamd_dkim_key_t*
+gboolean
 rspamd_get_dkim_key (rspamd_dkim_context_t *ctx, struct rspamd_dns_resolver *resolver,
-               struct rspamd_async_session *s)
+               struct rspamd_async_session *s, dkim_key_handler_f handler, gpointer ud)
 {
-       /* TODO: add this parser as well */
-       return NULL;
+       struct rspamd_dkim_key_cbdata                           *cbdata;
+
+       g_return_val_if_fail (ctx != NULL, FALSE);
+       g_return_val_if_fail (ctx->dns_key != NULL, FALSE);
+
+       cbdata = memory_pool_alloc (ctx->pool, sizeof (struct rspamd_dkim_key_cbdata));
+       cbdata->ctx = ctx;
+       cbdata->handler = handler;
+       cbdata->ud = ud;
+
+       return make_dns_request (resolver, s, ctx->pool, rspamd_dkim_dns_cb, cbdata, DNS_REQUEST_TXT, ctx->dns_key);
 }
 
 /**
index a7ad514bfc6e0510dad37248f8aad14bab794bda..6bb26887d9d8e00bcdee84bcea1548321709e1c6 100644 (file)
@@ -136,19 +136,20 @@ typedef struct rspamd_dkim_context_s {
        gchar *selector;
        time_t timestamp;
        time_t expiration;
-       gchar *b;
-       gchar *bh;
+       gint8 *b;
+       gint8 *bh;
        GList *hlist;
        guint ver;
+       gchar *dns_key;
 } rspamd_dkim_context_t;
 
-typedef struct rspamd_dkim_key_s {
-       memory_pool_t *pool;
-       /* TODO: make this structure */
-} rspamd_dkim_key_t;
+typedef guint8 rspamd_dkim_key_t;
 
 struct worker_task;
 
+/* Err MUST be freed if it is not NULL, key is allocated by slice allocator */
+typedef void (*dkim_key_handler_f)(rspamd_dkim_key_t *key, gsize keylen, rspamd_dkim_context_t *ctx, gpointer ud, GError *err);
+
 /**
  * Create new dkim context from signature
  * @param sig message's signature
@@ -165,8 +166,8 @@ rspamd_dkim_context_t* rspamd_create_dkim_context (const gchar *sig, memory_pool
  * @param s async session to make request
  * @return
  */
-rspamd_dkim_key_t* rspamd_get_dkim_key (rspamd_dkim_context_t *ctx, struct rspamd_dns_resolver *resolver,
-               struct rspamd_async_session *s);
+gboolean rspamd_get_dkim_key (rspamd_dkim_context_t *ctx, struct rspamd_dns_resolver *resolver,
+               struct rspamd_async_session *s, dkim_key_handler_f handler, gpointer ud);
 
 /**
  * Check task for dkim context using dkim key