diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2017-09-14 19:11:33 +0100 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2017-09-15 09:17:50 +0100 |
commit | b62c1a9f357bc17540bd85534c6deec61d85e9e2 (patch) | |
tree | de67709d7d2944558fa1417b19aff568868d19d1 | |
parent | 0fd5ec7e1e0da863189389cdd4746a2e2cae7f26 (diff) | |
download | rspamd-b62c1a9f357bc17540bd85534c6deec61d85e9e2.tar.gz rspamd-b62c1a9f357bc17540bd85534c6deec61d85e9e2.zip |
[Feature] Improve parsing of DKIM keys: parse algorithm
-rw-r--r-- | src/libserver/dkim.c | 115 |
1 files changed, 84 insertions, 31 deletions
diff --git a/src/libserver/dkim.c b/src/libserver/dkim.c index d98befc14..ecbddb89a 100644 --- a/src/libserver/dkim.c +++ b/src/libserver/dkim.c @@ -1297,58 +1297,111 @@ static rspamd_dkim_key_t * rspamd_dkim_parse_key (rspamd_dkim_context_t *ctx, const gchar *txt, gsize *keylen, GError **err) { - const gchar *c, *p, *end; - gint state = 0; - gsize len; + const gchar *c, *p, *end, *key = NULL, *alg = "rsa"; + enum { + read_tag = 0, + read_eqsign, + read_p_tag, + read_k_tag, + } state = read_tag; + gchar tag = '\0'; + gsize klen = 0, alglen = 0; c = txt; p = txt; end = txt + strlen (txt); - while (p <= end) { + 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; + case read_tag: + if (*p == '=') { + state = read_eqsign; + } else { + tag = *p; } - else { - /* Ignore everything */ + p++; + break; + case read_eqsign: + if (tag == 'p') { + state = read_p_tag; + c = p; + } else if (tag == 'k') { + state = read_k_tag; + c = p; + } else { + /* Unknown tag, ignore */ + state = read_tag; + tag = '\0'; p++; } break; - case 1: - /* State when we got p= and looking for some public key */ - if ((*p == ';' || p == end) && p > c) { - len = p - c; - - if (keylen) { - *keylen = len; - } - - return rspamd_dkim_make_key (ctx, c, len, - RSPAMD_DKIM_KEY_RSA, err); + case read_p_tag: + if (*p == ';') { + klen = p - c; + key = c; + state = read_tag; + tag = '\0'; } - else { - p++; + p++; + break; + case read_k_tag: + if (*p == ';') { + alglen = p - c; + alg = c; + state = read_tag; + tag = '\0'; } + p++; + break; + default: break; } } - if (p - c == 0) { + /* Leftover */ + switch (state) { + case read_p_tag: + klen = p - c; + key = c; + break; + case read_k_tag: + alglen = p - c; + alg = c; + break; + default: + break; + } + + if (klen == 0 || key == NULL) { g_set_error (err, - DKIM_ERROR, - DKIM_SIGERROR_KEYREVOKED, - "key was revoked"); + DKIM_ERROR, + DKIM_SIGERROR_KEYFAIL, + "key is missing"); + } + + if (alglen == 0 || alg == NULL) { + alg = "rsa"; /* Implicit */ + } + + if (alglen == 8 && rspamd_lc_cmp (alg, "ecdsa256", alglen) == 0) { + if (keylen) { + *keylen = klen; + } + + return rspamd_dkim_make_key (ctx, c, klen, + RSPAMD_DKIM_KEY_ECDSA, err); } else { - g_set_error (err, DKIM_ERROR, DKIM_SIGERROR_KEYFAIL, - "key was not found"); + /* We assume RSA default in all cases */ + if (alglen != 3 || rspamd_lc_cmp (alg, "rsa", alglen) != 0) { + msg_info_dkim ("invalid key algorithm: %*s", (gint)alglen, alg); + } + return rspamd_dkim_make_key (ctx, c, klen, + RSPAMD_DKIM_KEY_RSA, err); } + g_assert_not_reached (); + return NULL; } |