diff options
-rw-r--r-- | src/dkim.c | 126 | ||||
-rw-r--r-- | src/dkim.h | 17 |
2 files changed, 129 insertions, 14 deletions
diff --git a/src/dkim.c b/src/dkim.c index 8a25a08c2..97fd8db7e 100644 --- a/src/dkim.c +++ b/src/dkim.c @@ -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); } /** diff --git a/src/dkim.h b/src/dkim.h index a7ad514bf..6bb26887d 100644 --- a/src/dkim.h +++ b/src/dkim.h @@ -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 |