#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 */
+#define RSPAMD_DKIM_MAX_ARC_IDX 10
#define msg_err_dkim(...) rspamd_default_log_function (G_LOG_LEVEL_CRITICAL, \
"dkim", ctx->pool->tag.uid, \
struct rspamd_dkim_header {
- gchar *name;
+ const gchar *name;
guint count;
};
}
else {
/* Insert new header to the list */
- g_hash_table_insert (htb, new->name, new);
+ g_hash_table_insert (htb, (gpointer)new->name, new);
}
c = p + 1;
return TRUE;
}
+static void
+rspamd_dkim_add_arc_seal_headers (rspamd_mempool_t *pool,
+ struct rspamd_dkim_common_ctx *ctx)
+{
+ struct rspamd_dkim_header *hdr;
+ guint count = ctx->idx + 1, i;
+
+ ctx->hlist = g_ptr_array_sized_new (count * 3 - 1);
+
+ for (i = 0; i < count; i ++) {
+ /* Authentication results */
+ hdr = rspamd_mempool_alloc (pool, sizeof (*hdr));
+ hdr->name = RSPAMD_DKIM_ARC_AUTHHEADER;
+ hdr->count = i + 1;
+ g_ptr_array_add (ctx->hlist, hdr);
+
+ /* Arc signature */
+ hdr = rspamd_mempool_alloc (pool, sizeof (*hdr));
+ hdr->name = RSPAMD_DKIM_ARC_SIGNHEADER;
+ hdr->count = i + 1;
+ g_ptr_array_add (ctx->hlist, hdr);
+
+ /* Arc seal (except last one) */
+ if (i != count - 1) {
+ hdr = rspamd_mempool_alloc (pool, sizeof (*hdr));
+ hdr->name = RSPAMD_DKIM_ARC_SEALHEADER;
+ hdr->count = i + 1;
+ g_ptr_array_add (ctx->hlist, hdr);
+ }
+ }
+}
+
/**
* Create new dkim context from signature
* @param sig message's signature
/* Simple tags */
switch (*tag) {
case 'v':
- param = DKIM_PARAM_VERSION;
+ if (type == RSPAMD_DKIM_NORMAL) {
+ param = DKIM_PARAM_VERSION;
+ }
+ else {
+ g_set_error (err,
+ DKIM_ERROR,
+ DKIM_SIGERROR_UNKNOWN,
+ "invalid ARC v param");
+ state = DKIM_STATE_ERROR;
+ break;
+ }
break;
case 'a':
param = DKIM_PARAM_SIGNALG;
param = DKIM_PARAM_DOMAIN;
break;
case 'h':
- param = DKIM_PARAM_HDRLIST;
+ if (type == RSPAMD_DKIM_ARC_SEAL) {
+ g_set_error (err,
+ DKIM_ERROR,
+ DKIM_SIGERROR_UNKNOWN,
+ "ARC seal must NOT have h= tag");
+ state = DKIM_STATE_ERROR;
+ break;
+ }
+ else {
+ param = DKIM_PARAM_HDRLIST;
+ }
break;
case 'i':
if (type == RSPAMD_DKIM_NORMAL) {
break;
case 2:
if (tag[0] == 'b' && tag[1] == 'h') {
- param = DKIM_PARAM_BODYHASH;
+ if (type == RSPAMD_DKIM_ARC_SEAL) {
+ g_set_error (err,
+ DKIM_ERROR,
+ DKIM_SIGERROR_UNKNOWN,
+ "ARC seal must NOT have bh= tag");
+ state = DKIM_STATE_ERROR;
+ break;
+ }
+ else {
+ param = DKIM_PARAM_BODYHASH;
+ }
}
else {
g_set_error (err,
}
}
+ if (type == RSPAMD_DKIM_ARC_SEAL) {
+ rspamd_dkim_add_arc_seal_headers (pool, &ctx->common);
+ }
+
/* Now check validity of signature */
if (ctx->b == NULL) {
g_set_error (err,
"b parameter missing");
return NULL;
}
- if (ctx->bh == NULL) {
+ if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL && ctx->bh == NULL) {
g_set_error (err,
DKIM_ERROR,
DKIM_SIGERROR_EMPTY_BH,
"selector parameter missing");
return NULL;
}
- if (ctx->ver == 0) {
+ if (ctx->common.type == RSPAMD_DKIM_NORMAL && ctx->ver == 0) {
g_set_error (err,
DKIM_ERROR,
DKIM_SIGERROR_EMPTY_V,
return NULL;
}
+ if (ctx->common.type != RSPAMD_DKIM_NORMAL && (ctx->common.idx == 0 ||
+ ctx->common.idx > RSPAMD_DKIM_MAX_ARC_IDX)) {
+ g_set_error (err,
+ DKIM_ERROR,
+ DKIM_SIGERROR_UNKNOWN,
+ "i parameter missing or invalid for ARC");
+ return NULL;
+ }
+
/* Now create dns key to request further */
taglen = strlen (ctx->domain) + strlen (ctx->selector) +
sizeof (DKIM_DNSKEYNAME) + 2;
/* We need to find our own signature and use it */
guint i;
- ar = g_hash_table_lookup (task->raw_headers, DKIM_SIGNHEADER);
+ ar = g_hash_table_lookup (task->raw_headers, header_name);
if (ar) {
/* We need to find our own signature */
return DKIM_RECORD_ERROR;
}
- /* Start canonization of body part */
- if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end, FALSE)) {
- return DKIM_RECORD_ERROR;
+ if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
+ /* Start canonization of body part */
+ if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end,
+ FALSE)) {
+ return DKIM_RECORD_ERROR;
+ }
}
+
/* Now canonize headers */
for (i = 0; i < ctx->common.hlist->len; i++) {
dh = g_ptr_array_index (ctx->common.hlist, i);
}
/* Canonize dkim signature */
- rspamd_dkim_canonize_header (&ctx->common, task, DKIM_SIGNHEADER, 0,
- ctx->dkim_header, ctx->domain);
-
- dlen = EVP_MD_CTX_size (ctx->common.body_hash);
- /* Copy md_ctx to deal with broken CRLF at the end */
- cpy_ctx = EVP_MD_CTX_create ();
- EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
- EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);
-
- /* Check bh field */
- if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
- msg_debug_dkim ("bh value mismatch: %*xs versus %*xs, try add CRLF",
- dlen, ctx->bh,
- dlen, raw_digest);
- /* Try add CRLF */
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- EVP_MD_CTX_cleanup (cpy_ctx);
-#else
- EVP_MD_CTX_reset (cpy_ctx);
-#endif
+ switch (ctx->common.type) {
+ case RSPAMD_DKIM_NORMAL:
+ rspamd_dkim_canonize_header (&ctx->common, task, RSPAMD_DKIM_SIGNHEADER, 0,
+ ctx->dkim_header, ctx->domain);
+ break;
+ case RSPAMD_DKIM_ARC_SIG:
+ rspamd_dkim_canonize_header (&ctx->common, task, RSPAMD_DKIM_ARC_SIGNHEADER, 0,
+ ctx->dkim_header, ctx->domain);
+ break;
+ case RSPAMD_DKIM_ARC_SEAL:
+ rspamd_dkim_canonize_header (&ctx->common, task, RSPAMD_DKIM_ARC_SEALHEADER, 0,
+ ctx->dkim_header, ctx->domain);
+ break;
+ }
+
+
+ if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
+ dlen = EVP_MD_CTX_size (ctx->common.body_hash);
+ /* Copy md_ctx to deal with broken CRLF at the end */
+ cpy_ctx = EVP_MD_CTX_create ();
EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
- EVP_DigestUpdate (cpy_ctx, "\r\n", 2);
EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);
+ /* Check bh field */
if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
- msg_debug_dkim ("bh value mismatch: %*xs versus %*xs, try add LF",
+ msg_debug_dkim ("bh value mismatch: %*xs versus %*xs, try add CRLF",
dlen, ctx->bh,
dlen, raw_digest);
-
- /* Try add LF */
- #if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ /* Try add CRLF */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
EVP_MD_CTX_cleanup (cpy_ctx);
- #else
+#else
EVP_MD_CTX_reset (cpy_ctx);
- #endif
+#endif
EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
- EVP_DigestUpdate (cpy_ctx, "\n", 1);
+ EVP_DigestUpdate (cpy_ctx, "\r\n", 2);
EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);
if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
- msg_debug_dkim ("bh value mismatch: %*xs versus %*xs",
+ msg_debug_dkim (
+ "bh value mismatch: %*xs versus %*xs, try add LF",
dlen, ctx->bh,
dlen, raw_digest);
+
+ /* Try add LF */
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
EVP_MD_CTX_cleanup (cpy_ctx);
#else
EVP_MD_CTX_reset (cpy_ctx);
#endif
- EVP_MD_CTX_destroy (cpy_ctx);
- return DKIM_REJECT;
+ EVP_MD_CTX_copy (cpy_ctx, ctx->common.body_hash);
+ EVP_DigestUpdate (cpy_ctx, "\n", 1);
+ EVP_DigestFinal_ex (cpy_ctx, raw_digest, NULL);
+
+ if (memcmp (ctx->bh, raw_digest, ctx->bhlen) != 0) {
+ msg_debug_dkim ("bh value mismatch: %*xs versus %*xs",
+ dlen, ctx->bh,
+ dlen, raw_digest);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+ EVP_MD_CTX_cleanup (cpy_ctx);
+#else
+ EVP_MD_CTX_reset (cpy_ctx);
+#endif
+ EVP_MD_CTX_destroy (cpy_ctx);
+ return DKIM_REJECT;
+ }
}
}
- }
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
- EVP_MD_CTX_cleanup (cpy_ctx);
+ EVP_MD_CTX_cleanup (cpy_ctx);
#else
- EVP_MD_CTX_reset (cpy_ctx);
+ EVP_MD_CTX_reset (cpy_ctx);
#endif
- EVP_MD_CTX_destroy (cpy_ctx);
+ EVP_MD_CTX_destroy (cpy_ctx);
+ }
dlen = EVP_MD_CTX_size (ctx->common.headers_hash);
EVP_DigestFinal_ex (ctx->common.headers_hash, raw_digest, NULL);
nctx->common.body_canon_type = body_canon;
nctx->common.type = type;
- if (!rspamd_dkim_parse_hdrlist_common (&nctx->common, headers, strlen (headers),
- err)) {
- return NULL;
+ if (type != RSPAMD_DKIM_ARC_SEAL) {
+ if (!rspamd_dkim_parse_hdrlist_common (&nctx->common, headers,
+ strlen (headers),
+ err)) {
+ return NULL;
+ }
+ }
+ else {
+ rspamd_dkim_add_arc_seal_headers (task->task_pool, &nctx->common);
}
nctx->key = rspamd_dkim_sign_key_ref (priv_key);
{
GString *hdr;
struct rspamd_dkim_header *dh;
- const gchar *body_end, *body_start;
+ const gchar *body_end, *body_start, *hname;
guchar raw_digest[EVP_MAX_MD_SIZE];
gsize dlen;
guint i, j;
}
/* Start canonization of body part */
- if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end, TRUE)) {
- return NULL;
+ if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
+ if (!rspamd_dkim_canonize_body (&ctx->common, body_start, body_end,
+ TRUE)) {
+ return NULL;
+ }
}
hdr = g_string_sized_new (255);
}
else {
/* Shouldn't be called for arc seal */
- g_assert_not_reached ();
+ rspamd_printf_gstring (hdr, "i=%d; a=rsa-sha256; c=%s/%s; d=%s; s=%s; ",
+ idx,
+ domain, selector);
}
if (expire > 0) {
rspamd_printf_gstring (hdr, "x=%t; ", expire);
}
- if (len > 0) {
- rspamd_printf_gstring (hdr, "l=%z; ", len);
+ if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
+ if (len > 0) {
+ rspamd_printf_gstring (hdr, "l=%z; ", len);
+ }
}
rspamd_printf_gstring (hdr, "t=%t; h=", time (NULL));
/* Replace the last ':' with ';' */
hdr->str[hdr->len - 1] = ';';
- dlen = EVP_MD_CTX_size (ctx->common.body_hash);
- EVP_DigestFinal_ex (ctx->common.body_hash, raw_digest, NULL);
+ if (ctx->common.type != RSPAMD_DKIM_ARC_SEAL) {
+ dlen = EVP_MD_CTX_size (ctx->common.body_hash);
+ EVP_DigestFinal_ex (ctx->common.body_hash, raw_digest, NULL);
- b64_data = rspamd_encode_base64 (raw_digest, dlen, 0, NULL);
- rspamd_printf_gstring (hdr, " bh=%s; b=", b64_data);
- g_free (b64_data);
+ b64_data = rspamd_encode_base64 (raw_digest, dlen, 0, NULL);
+ rspamd_printf_gstring (hdr, " bh=%s; b=", b64_data);
+ g_free (b64_data);
+ }
+ else {
+ rspamd_printf_gstring (hdr, " b=");
+ }
+
+ switch (ctx->common.type) {
+ case RSPAMD_DKIM_NORMAL:
+ hname = RSPAMD_DKIM_SIGNHEADER;
+ break;
+ case RSPAMD_DKIM_ARC_SIG:
+ hname = RSPAMD_DKIM_ARC_SIGNHEADER;
+ break;
+ case RSPAMD_DKIM_ARC_SEAL:
+ hname = RSPAMD_DKIM_ARC_SEALHEADER;
+ break;
+ }
if (ctx->common.header_canon_type == DKIM_CANON_RELAXED) {
if (!rspamd_dkim_canonize_header_relaxed (&ctx->common,
hdr->str,
- DKIM_SIGNHEADER,
+ hname,
TRUE)) {
g_string_free (hdr, TRUE);