summaryrefslogtreecommitdiffstats
path: root/src/dkim.c
diff options
context:
space:
mode:
authorVsevolod Stakhov <vsevolod@rambler-co.ru>2012-05-25 20:14:18 +0400
committerVsevolod Stakhov <vsevolod@rambler-co.ru>2012-05-25 20:14:18 +0400
commit5a0ad4684967e746b71f635f397ea4c0ae373f69 (patch)
tree5f6b5c4d8d5cec4c562dab2cad761ea4daad2d6d /src/dkim.c
parent3f333be9d531f0edecf3f31a241a8692e79a86bd (diff)
downloadrspamd-5a0ad4684967e746b71f635f397ea4c0ae373f69.tar.gz
rspamd-5a0ad4684967e746b71f635f397ea4c0ae373f69.zip
Implement key extracting for dkim records.
Diffstat (limited to 'src/dkim.c')
-rw-r--r--src/dkim.c126
1 files changed, 120 insertions, 6 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);
}
/**