]> git.ipfire.org Git - thirdparty/rspamd.git/commitdiff
[Feature] Improve parsing of DKIM keys: parse algorithm
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Thu, 14 Sep 2017 18:11:33 +0000 (19:11 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Fri, 15 Sep 2017 08:17:50 +0000 (09:17 +0100)
src/libserver/dkim.c

index d98befc14cd4208cfb898198b9296ce52bbff072..ecbddb89a380e95e48dcf2a14e53317c1c0ec1e4 100644 (file)
@@ -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;
 }