From 3f333be9d531f0edecf3f31a241a8692e79a86bd Mon Sep 17 00:00:00 2001 From: Vsevolod Stakhov Date: Thu, 24 May 2012 21:35:03 +0400 Subject: [PATCH] * Start dkim support implementation. --- lib/CMakeLists.txt | 1 + src/dkim.c | 536 +++++++++++++++++++++++++++++++++++++++++++++ src/dkim.h | 180 +++++++++++++++ 3 files changed, 717 insertions(+) create mode 100644 src/dkim.c create mode 100644 src/dkim.h diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index feeab47ca..a0350e012 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -43,6 +43,7 @@ SET(RSPAMDLIBSRC ../src/cfg_utils.c ../src/cfg_xml.c ../src/diff.c + ../src/dkim.c ../src/dns.c ../src/events.c ../src/expressions.c diff --git a/src/dkim.c b/src/dkim.c new file mode 100644 index 000000000..8a25a08c2 --- /dev/null +++ b/src/dkim.c @@ -0,0 +1,536 @@ +/* Copyright (c) 2010-2011, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "main.h" +#include "dkim.h" + +/* Parser of dkim params */ +typedef gboolean (*dkim_parse_param_f) (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); + +static gboolean rspamd_dkim_parse_signature (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_signalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_domain (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_canonalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_ignore (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_selector (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_hdrlist (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_version (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_timestamp (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_expiration (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_bodyhash (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); +static gboolean rspamd_dkim_parse_bodylength (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err); + + +static const dkim_parse_param_f parser_funcs[] = { + [DKIM_PARAM_SIGNATURE] = rspamd_dkim_parse_signature, + [DKIM_PARAM_SIGNALG] = rspamd_dkim_parse_signalg, + [DKIM_PARAM_DOMAIN] = rspamd_dkim_parse_domain, + [DKIM_PARAM_CANONALG] = rspamd_dkim_parse_canonalg, + [DKIM_PARAM_QUERYMETHOD] = rspamd_dkim_parse_ignore, + [DKIM_PARAM_SELECTOR] = rspamd_dkim_parse_selector, + [DKIM_PARAM_HDRLIST] = rspamd_dkim_parse_hdrlist, + [DKIM_PARAM_VERSION] = rspamd_dkim_parse_version, + [DKIM_PARAM_IDENTITY] = rspamd_dkim_parse_ignore, + [DKIM_PARAM_TIMESTAMP] = rspamd_dkim_parse_timestamp, + [DKIM_PARAM_EXPIRATION] = rspamd_dkim_parse_expiration, + [DKIM_PARAM_COPIEDHDRS] = rspamd_dkim_parse_ignore, + [DKIM_PARAM_BODYHASH] = rspamd_dkim_parse_bodyhash, + [DKIM_PARAM_BODYLENGTH] = rspamd_dkim_parse_bodylength +}; + +#define DKIM_ERROR dkim_error_quark () +GQuark +dkim_error_quark (void) +{ + return g_quark_from_static_string ("dkim-error-quark"); +} + +/* Parsers implementation */ +static gboolean +rspamd_dkim_parse_signature (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + ctx->b = memory_pool_alloc (ctx->pool, len + 1); + rspamd_strlcpy (ctx->b, param, len + 1); + g_base64_decode_inplace (ctx->b, &len); + return TRUE; +} + +static gboolean +rspamd_dkim_parse_signalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + if (len == 8) { + if (memcmp (param, "rsa-sha1", len) == 0) { + ctx->sig_alg = DKIM_SIGN_RSASHA1; + return TRUE; + } + } + else if (len == 10) { + if (memcmp (param, "rsa-sha256", len) == 0) { + ctx->sig_alg = DKIM_SIGN_RSASHA256; + return TRUE; + } + } + + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_A, "invalid dkim sign algorithm"); + return FALSE; +} + +static gboolean +rspamd_dkim_parse_domain (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + ctx->domain = memory_pool_alloc (ctx->pool, len + 1); + rspamd_strlcpy (ctx->domain, param, len + 1); + return TRUE; +} + +static gboolean +rspamd_dkim_parse_canonalg (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + const gchar *p, *slash = NULL, *end = param + len; + gsize sl = 0; + + p = param; + while (p != end) { + if (*p == '/') { + slash = p; + break; + } + p ++; + sl ++; + } + + if (slash == NULL) { + /* Only check header */ + if (len == 6 && memcmp (param, "simple", len) == 0) { + ctx->header_canon_type = DKIM_CANON_SIMPLE; + return TRUE; + } + else if (len == 7 && memcmp (param, "relaxed", len) == 0) { + ctx->header_canon_type = DKIM_CANON_RELAXED; + return TRUE; + } + } + else { + /* First check header */ + if (sl == 6 && memcmp (param, "simple", len) == 0) { + ctx->header_canon_type = DKIM_CANON_SIMPLE; + } + else if (sl == 7 && memcmp (param, "relaxed", len) == 0) { + ctx->header_canon_type = DKIM_CANON_RELAXED; + } + else { + goto err; + } + /* Check body */ + len = len - sl - 1; + slash ++; + if (len == 6 && memcmp (slash, "simple", len) == 0) { + ctx->body_canon_type = DKIM_CANON_SIMPLE; + return TRUE; + } + else if (len == 7 && memcmp (slash, "relaxed", len) == 0) { + ctx->body_canon_type = DKIM_CANON_RELAXED; + return TRUE; + } + } + +err: + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_A, "invalid dkim sign algorithm"); + return FALSE; +} + +static gboolean +rspamd_dkim_parse_ignore (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + /* Just ignore unused params */ + return TRUE; +} + +static gboolean +rspamd_dkim_parse_selector (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + ctx->selector = memory_pool_alloc (ctx->pool, len + 1); + rspamd_strlcpy (ctx->selector, param, len + 1); + return TRUE; +} + +static gboolean +rspamd_dkim_parse_hdrlist (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + const gchar *c, *p, *end = param + len; + gchar *h; + gboolean from_found = FALSE; + + c = param; + p = param; + while (p <= end) { + if ((*p == ':' || p == end) && p - c > 0) { + /* Insert new header to the list */ + if (p == end) { + h = memory_pool_alloc (ctx->pool, p - c + 1); + rspamd_strlcpy (h, c, p - c + 1); + } + else { + h = memory_pool_alloc (ctx->pool, p - c); + rspamd_strlcpy (h, c, p - c); + } + /* Check mandatory from */ + if (!from_found && g_ascii_strcasecmp (h, "from") == 0) { + from_found = TRUE; + } + ctx->hlist = g_list_prepend (ctx->hlist, h); + c = p + 1; + p ++; + } + else { + p ++; + } + } + + if (!ctx->hlist) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_H, "invalid dkim header list"); + return FALSE; + } + else { + if (!from_found) { + g_list_free (ctx->hlist); + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_H, "invalid dkim header list, from header is missing"); + return FALSE; + } + /* Reverse list */ + ctx->hlist = g_list_reverse (ctx->hlist); + memory_pool_add_destructor (ctx->pool, (pool_destruct_func)g_list_free, ctx->hlist); + } + + return TRUE; +} + +static gboolean +rspamd_dkim_parse_version (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + if (len != 1 || *param != '1') { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_VERSION, "invalid dkim version"); + return FALSE; + } + + ctx->ver = 1; + return TRUE; +} + +static gboolean +rspamd_dkim_parse_timestamp (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + gulong val; + + if (!rspamd_strtoul (param, len, &val)) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim timestamp"); + return FALSE; + } + ctx->timestamp = val; + + return TRUE; +} + +static gboolean +rspamd_dkim_parse_expiration (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + gulong val; + + if (!rspamd_strtoul (param, len, &val)) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim expiration"); + return FALSE; + } + ctx->expiration = val; + + return TRUE; +} + +static gboolean +rspamd_dkim_parse_bodyhash (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + ctx->bh = memory_pool_alloc (ctx->pool, len + 1); + rspamd_strlcpy (ctx->bh, param, len + 1); + g_base64_decode_inplace (ctx->bh, &len); + return TRUE; +} + +static gboolean +rspamd_dkim_parse_bodylength (rspamd_dkim_context_t* ctx, const gchar *param, gsize len, GError **err) +{ + gulong val; + + if (!rspamd_strtoul (param, len, &val)) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_INVALID_L, "invalid dkim body length"); + return FALSE; + } + ctx->len = val; + + return TRUE; +} + +/** + * Create new dkim context from signature + * @param sig message's signature + * @param pool pool to allocate memory from + * @param err pointer to error object + * @return new context or NULL + */ +rspamd_dkim_context_t* +rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err) +{ + const gchar *p, *c, *tag; + gsize taglen; + gint param = DKIM_PARAM_UNKNOWN; + time_t now; + rspamd_dkim_context_t *new; + enum { + DKIM_STATE_TAG = 0, + DKIM_STATE_AFTER_TAG, + DKIM_STATE_VALUE, + DKIM_STATE_SKIP_SPACES = 99, + DKIM_STATE_ERROR = 100 + } state, next_state; + + + new = memory_pool_alloc0 (pool, sizeof (rspamd_dkim_context_t)); + new->pool = pool; + new->header_canon_type = DKIM_CANON_DEFAULT; + new->body_canon_type = DKIM_CANON_DEFAULT; + new->sig_alg = DKIM_SIGN_UNKNOWN; + /* A simple state machine of parsing tags */ + state = DKIM_STATE_SKIP_SPACES; + next_state = DKIM_STATE_TAG; + taglen = 0; + while (*p) { + switch (state) { + case DKIM_STATE_TAG: + if (g_ascii_isspace (*p)) { + taglen = p - c; + while (*p && g_ascii_isspace (*p)) { + /* Skip spaces before '=' sign */ + p ++; + } + if (*p != '=') { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param"); + state = DKIM_STATE_ERROR; + } + else { + state = DKIM_STATE_SKIP_SPACES; + next_state = DKIM_STATE_AFTER_TAG; + param = DKIM_PARAM_UNKNOWN; + p ++; + tag = c; + } + } + else if (*p == '=') { + state = DKIM_STATE_SKIP_SPACES; + next_state = DKIM_STATE_AFTER_TAG; + param = DKIM_PARAM_UNKNOWN; + p ++; + tag = c; + } + else { + p ++; + taglen ++; + } + break; + case DKIM_STATE_AFTER_TAG: + /* We got tag at tag and len at taglen */ + switch (taglen) { + case 0: + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "zero length dkim param"); + state = DKIM_STATE_ERROR; + break; + case 1: + /* Simple tags */ + switch (*tag) { + case 'v': + param = DKIM_PARAM_VERSION; + break; + case 'a': + param = DKIM_PARAM_SIGNALG; + break; + case 'b': + param = DKIM_PARAM_SIGNATURE; + break; + case 'c': + param = DKIM_PARAM_CANONALG; + break; + case 'd': + param = DKIM_PARAM_DOMAIN; + break; + case 'h': + param = DKIM_PARAM_HDRLIST; + break; + case 'i': + param = DKIM_PARAM_IDENTITY; + break; + case 'l': + param = DKIM_PARAM_BODYLENGTH; + break; + case 'q': + param = DKIM_PARAM_QUERYMETHOD; + break; + case 's': + param = DKIM_PARAM_SELECTOR; + break; + case 't': + param = DKIM_PARAM_TIMESTAMP; + break; + case 'x': + param = DKIM_PARAM_EXPIRATION; + break; + case 'z': + param = DKIM_PARAM_COPIEDHDRS; + break; + default: + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param: %c", *tag); + state = DKIM_STATE_ERROR; + break; + } + break; + case 2: + if (tag[0] == 'b' && tag[1] == 'h') { + param = DKIM_PARAM_BODYHASH; + } + else { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param: %c%c", tag[0], tag[1]); + state = DKIM_STATE_ERROR; + } + break; + default: + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_UNKNOWN, "invalid dkim param lenght: %zd", taglen); + state = DKIM_STATE_ERROR; + break; + } + if (state != DKIM_STATE_ERROR) { + /* Skip spaces */ + p ++; + state = DKIM_STATE_SKIP_SPACES; + next_state = DKIM_STATE_VALUE; + } + break; + case DKIM_STATE_VALUE: + if (*p == ';') { + if (param == DKIM_PARAM_UNKNOWN || !parser_funcs[param](new, c, p - c - 1, err)) { + state = DKIM_STATE_ERROR; + } + } + else { + p ++; + } + break; + case DKIM_STATE_SKIP_SPACES: + if (g_ascii_isspace (*p)) { + p ++; + } + else { + c = p; + state = next_state; + } + break; + case DKIM_STATE_ERROR: + if (err) { + msg_info ("dkim parse failed: %s", (*err)->message); + return NULL; + } + else { + msg_info ("dkim parse failed: unknown error"); + return NULL; + } + break; + } + } + + /* Now check validity of signature */ + if (new->b == NULL) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_B, "b parameter missing"); + return NULL; + } + if (new->bh == NULL) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_BH, "bh parameter missing"); + return NULL; + } + if (new->domain == NULL) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_D, "domain parameter missing"); + return NULL; + } + if (new->selector == NULL) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_S, "selector parameter missing"); + return NULL; + } + if (new->ver == 0) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_V, "v parameter missing"); + return NULL; + } + if (new->hlist == NULL) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_H, "h parameter missing"); + return NULL; + } + if (new->sig_alg == DKIM_SIGN_UNKNOWN) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EMPTY_S, "s parameter missing"); + return NULL; + } + /* Check expiration */ + now = time (NULL); + if (new->timestamp && new->timestamp > now) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_FUTURE, "signature was made in future, ignoring"); + return NULL; + } + if (new->expiration && new->expiration < now) { + g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_EXPIRED, "signature has expired"); + return NULL; + } + + return new; +} + +/** + * Make DNS request for specified context and obtain and parse key + * @param ctx dkim context from signature + * @param resolver dns resolver object + * @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) +{ + /* TODO: add this parser as well */ + return NULL; +} + +/** + * Check task for dkim context using dkim key + * @param ctx dkim verify context + * @param key dkim key (from cache or from dns request) + * @param task task to check + * @return + */ +gint +rspamd_dkim_check (rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key, struct worker_task *task) +{ + /* TODO: this check must be implemented */ + return DKIM_CONTINUE; +} diff --git a/src/dkim.h b/src/dkim.h new file mode 100644 index 000000000..a7ad514bf --- /dev/null +++ b/src/dkim.h @@ -0,0 +1,180 @@ +/* Copyright (c) 2010-2011, Vsevolod Stakhov + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL AUTHOR BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef DKIM_H_ +#define DKIM_H_ + +#include "config.h" +#include "event.h" +#include "dns.h" + +/* Main types and definitions */ + +#define DKIM_SIGNHEADER "DKIM-Signature" + /* DKIM signature header */ + +/* special DNS tokens */ +#define DKIM_DNSKEYNAME "_domainkey" + /* reserved DNS sub-zone */ +#define DKIM_DNSPOLICYNAME "_adsp" /* reserved DNS sub-zone */ + +/* Canonization methods */ +#define DKIM_CANON_UNKNOWN (-1) /* unknown method */ +#define DKIM_CANON_SIMPLE 0 /* as specified in DKIM spec */ +#define DKIM_CANON_RELAXED 1 /* as specified in DKIM spec */ + +#define DKIM_CANON_DEFAULT DKIM_CANON_SIMPLE + +/* Signature methods */ +#define DKIM_SIGN_UNKNOWN (-2) /* unknown method */ +#define DKIM_SIGN_DEFAULT (-1) /* use internal default */ +#define DKIM_SIGN_RSASHA1 0 /* an RSA-signed SHA1 digest */ +#define DKIM_SIGN_RSASHA256 1 /* an RSA-signed SHA256 digest */ + +/* Params */ +#define DKIM_PARAM_UNKNOWN (-1) /* unknown */ +#define DKIM_PARAM_SIGNATURE 0 /* b */ +#define DKIM_PARAM_SIGNALG 1 /* a */ +#define DKIM_PARAM_DOMAIN 2 /* d */ +#define DKIM_PARAM_CANONALG 3 /* c */ +#define DKIM_PARAM_QUERYMETHOD 4 /* q */ +#define DKIM_PARAM_SELECTOR 5 /* s */ +#define DKIM_PARAM_HDRLIST 6 /* h */ +#define DKIM_PARAM_VERSION 7 /* v */ +#define DKIM_PARAM_IDENTITY 8 /* i */ +#define DKIM_PARAM_TIMESTAMP 9 /* t */ +#define DKIM_PARAM_EXPIRATION 10 /* x */ +#define DKIM_PARAM_COPIEDHDRS 11 /* z */ +#define DKIM_PARAM_BODYHASH 12 /* bh */ +#define DKIM_PARAM_BODYLENGTH 13 /* l */ + +/* Errors (from OpenDKIM) */ + +#define DKIM_SIGERROR_UNKNOWN (-1) /* unknown error */ +#define DKIM_SIGERROR_OK 0 /* no error */ +#define DKIM_SIGERROR_VERSION 1 /* unsupported version */ +#define DKIM_SIGERROR_DOMAIN 2 /* invalid domain (d=/i=) */ +#define DKIM_SIGERROR_EXPIRED 3 /* signature expired */ +#define DKIM_SIGERROR_FUTURE 4 /* signature in the future */ +#define DKIM_SIGERROR_TIMESTAMPS 5 /* x= < t= */ +#define DKIM_SIGERROR_UNUSED 6 /* OBSOLETE */ +#define DKIM_SIGERROR_INVALID_HC 7 /* c= invalid (header) */ +#define DKIM_SIGERROR_INVALID_BC 8 /* c= invalid (body) */ +#define DKIM_SIGERROR_MISSING_A 9 /* a= missing */ +#define DKIM_SIGERROR_INVALID_A 10 /* a= invalid */ +#define DKIM_SIGERROR_MISSING_H 11 /* h= missing */ +#define DKIM_SIGERROR_INVALID_L 12 /* l= invalid */ +#define DKIM_SIGERROR_INVALID_Q 13 /* q= invalid */ +#define DKIM_SIGERROR_INVALID_QO 14 /* q= option invalid */ +#define DKIM_SIGERROR_MISSING_D 15 /* d= missing */ +#define DKIM_SIGERROR_EMPTY_D 16 /* d= empty */ +#define DKIM_SIGERROR_MISSING_S 17 /* s= missing */ +#define DKIM_SIGERROR_EMPTY_S 18 /* s= empty */ +#define DKIM_SIGERROR_MISSING_B 19 /* b= missing */ +#define DKIM_SIGERROR_EMPTY_B 20 /* b= empty */ +#define DKIM_SIGERROR_CORRUPT_B 21 /* b= corrupt */ +#define DKIM_SIGERROR_NOKEY 22 /* no key found in DNS */ +#define DKIM_SIGERROR_DNSSYNTAX 23 /* DNS reply corrupt */ +#define DKIM_SIGERROR_KEYFAIL 24 /* DNS query failed */ +#define DKIM_SIGERROR_MISSING_BH 25 /* bh= missing */ +#define DKIM_SIGERROR_EMPTY_BH 26 /* bh= empty */ +#define DKIM_SIGERROR_CORRUPT_BH 27 /* bh= corrupt */ +#define DKIM_SIGERROR_BADSIG 28 /* signature mismatch */ +#define DKIM_SIGERROR_SUBDOMAIN 29 /* unauthorized subdomain */ +#define DKIM_SIGERROR_MULTIREPLY 30 /* multiple records returned */ +#define DKIM_SIGERROR_EMPTY_H 31 /* h= empty */ +#define DKIM_SIGERROR_INVALID_H 32 /* h= missing req'd entries */ +#define DKIM_SIGERROR_TOOLARGE_L 33 /* l= value exceeds body size */ +#define DKIM_SIGERROR_MBSFAILED 34 /* "must be signed" failure */ +#define DKIM_SIGERROR_KEYVERSION 35 /* unknown key version */ +#define DKIM_SIGERROR_KEYUNKNOWNHASH 36 /* unknown key hash */ +#define DKIM_SIGERROR_KEYHASHMISMATCH 37 /* sig-key hash mismatch */ +#define DKIM_SIGERROR_NOTEMAILKEY 38 /* not an e-mail key */ +#define DKIM_SIGERROR_UNUSED2 39 /* OBSOLETE */ +#define DKIM_SIGERROR_KEYTYPEMISSING 40 /* key type missing */ +#define DKIM_SIGERROR_KEYTYPEUNKNOWN 41 /* key type unknown */ +#define DKIM_SIGERROR_KEYREVOKED 42 /* key revoked */ +#define DKIM_SIGERROR_KEYDECODE 43 /* key couldn't be decoded */ +#define DKIM_SIGERROR_MISSING_V 44 /* v= tag missing */ +#define DKIM_SIGERROR_EMPTY_V 45 /* v= tag empty */ + +/* Check results */ +#define DKIM_CONTINUE 0 /* continue */ +#define DKIM_REJECT 1 /* reject */ +#define DKIM_TRYAGAIN 2 /* try again later */ +#define DKIM_NOTFOUND 3 /* requested record not found */ +#define DKIM_RECORD_ERROR 4 /* error requesting record */ + +typedef struct rspamd_dkim_context_s { + memory_pool_t *pool; + gint sig_alg; + gint header_canon_type; + gint body_canon_type; + gsize len; + gchar *domain; + gchar *selector; + time_t timestamp; + time_t expiration; + gchar *b; + gchar *bh; + GList *hlist; + guint ver; +} rspamd_dkim_context_t; + +typedef struct rspamd_dkim_key_s { + memory_pool_t *pool; + /* TODO: make this structure */ +} rspamd_dkim_key_t; + +struct worker_task; + +/** + * Create new dkim context from signature + * @param sig message's signature + * @param pool pool to allocate memory from + * @param err pointer to error object + * @return new context or NULL + */ +rspamd_dkim_context_t* rspamd_create_dkim_context (const gchar *sig, memory_pool_t *pool, GError **err); + +/** + * Make DNS request for specified context and obtain and parse key + * @param ctx dkim context from signature + * @param resolver dns resolver object + * @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); + +/** + * Check task for dkim context using dkim key + * @param ctx dkim verify context + * @param key dkim key (from cache or from dns request) + * @param task task to check + * @return + */ +gint rspamd_dkim_check (rspamd_dkim_context_t *ctx, rspamd_dkim_key_t *key, struct worker_task *task); + +#endif /* DKIM_H_ */ -- 2.39.5