Add SHA256 fingerprint support for both the normal exported fingerprints
(tls_digest_n -> tls_digest_sha256_n), as well as for --x509-track.

Also switch to using the SHA256 fingerprint instead of the SHA1 fingerprint
internally, in cert_hash_remember() / cert_hash_compare().  And instead of
updating an #if 0'd code block that has been disabled since 2009, just
remove that.

This should take care of trac #675.

v2: update openvpn.8 accordingly

Signed-off-by: Steffan Karger <stef...@karger.me>
---
 doc/openvpn.8                    |  5 ++--
 src/openvpn/ssl_verify.c         | 50 ++++++++++++++++------------------------
 src/openvpn/ssl_verify.h         |  2 +-
 src/openvpn/ssl_verify_backend.h | 25 +++++++++++++++-----
 src/openvpn/ssl_verify_mbedtls.c | 48 +++++++++++++++++++++++++++++---------
 src/openvpn/ssl_verify_openssl.c | 37 +++++++++++++++++++++--------
 6 files changed, 107 insertions(+), 60 deletions(-)

diff --git a/doc/openvpn.8 b/doc/openvpn.8
index 7d5dc5b..269803e 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -6347,9 +6347,8 @@ Set prior to execution of the
 script.
 .\"*********************************************************
 .TP
-.B tls_digest_{n}
-Contains the certificate SHA1 fingerprint/digest hash value,
-where
+.B tls_digest_{n} / tls_digest_sha256_{n}
+Contains the certificate SHA1 / SHA256 fingerprint, where
 .B n
 is the verification level.  Only set for TLS connections.  Set prior
 to execution of
diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c
index b373bed..15a5de8 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -191,7 +191,8 @@ tls_username (const struct tls_multi *multi, const bool 
null)
 }

 void
-cert_hash_remember (struct tls_session *session, const int error_depth, const 
unsigned char *sha1_hash)
+cert_hash_remember (struct tls_session *session, const int error_depth,
+    const struct buffer *cert_hash)
 {
   if (error_depth >= 0 && error_depth < MAX_CERT_DEPTH)
     {
@@ -201,31 +202,12 @@ cert_hash_remember (struct tls_session *session, const 
int error_depth, const un
        ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash);
       {
        struct cert_hash *ch = session->cert_hash_set->ch[error_depth];
-       memcpy (ch->sha1_hash, sha1_hash, SHA_DIGEST_LENGTH);
+       ASSERT (sizeof (ch->sha256_hash) == BLEN (cert_hash));
+       memcpy (ch->sha256_hash, BPTR (cert_hash), sizeof (ch->sha256_hash));
       }
     }
 }

-#if 0
-static void
-cert_hash_print (const struct cert_hash_set *chs, int msglevel)
-{
-  struct gc_arena gc = gc_new ();
-  msg (msglevel, "CERT_HASH");
-  if (chs)
-    {
-      int i;
-      for (i = 0; i < MAX_CERT_DEPTH; ++i)
-       {
-         const struct cert_hash *ch = chs->ch[i];
-         if (ch)
-           msg (msglevel, "%d:%s", i, format_hex(ch->sha1_hash, 
SHA_DIGEST_LENGTH, 0, &gc));
-       }
-    }
-  gc_free (&gc);
-}
-#endif
-
 void
 cert_hash_free (struct cert_hash_set *chs)
 {
@@ -251,7 +233,8 @@ cert_hash_compare (const struct cert_hash_set *chs1, const 
struct cert_hash_set

          if (!ch1 && !ch2)
            continue;
-         else if (ch1 && ch2 && !memcmp (ch1->sha1_hash, ch2->sha1_hash, 
SHA_DIGEST_LENGTH))
+         else if (ch1 && ch2 && !memcmp (ch1->sha256_hash, ch2->sha256_hash,
+             sizeof(ch1->sha256_hash)))
            continue;
          else
            return false;
@@ -278,7 +261,8 @@ cert_hash_copy (const struct cert_hash_set *chs)
          if (ch)
            {
              ALLOC_OBJ (dest->ch[i], struct cert_hash);
-             memcpy (dest->ch[i]->sha1_hash, ch->sha1_hash, SHA_DIGEST_LENGTH);
+             memcpy (dest->ch[i]->sha256_hash, ch->sha256_hash,
+                 sizeof(dest->ch[i]->sha256_hash));
            }
        }
     }
@@ -416,13 +400,19 @@ verify_cert_set_env(struct env_set *es, 
openvpn_x509_cert_t *peer_cert, int cert
   setenv_str (es, envname, common_name);
 #endif

-  /* export X509 cert SHA1 fingerprint */
+  /* export X509 cert fingerprints */
   {
-    unsigned char *sha1_hash = x509_get_sha1_hash(peer_cert, &gc);
+    struct buffer sha1 = x509_get_sha1_fingerprint(peer_cert, &gc);
+    struct buffer sha256 = x509_get_sha256_fingerprint(peer_cert, &gc);

     openvpn_snprintf (envname, sizeof(envname), "tls_digest_%d", cert_depth);
-    setenv_str (es, envname, format_hex_ex(sha1_hash, SHA_DIGEST_LENGTH, 0, 1,
-                                         ":", &gc));
+    setenv_str (es, envname,
+       format_hex_ex(BPTR(&sha1), BLEN(&sha1), 0, 1, ":", &gc));
+
+    openvpn_snprintf (envname, sizeof(envname), "tls_digest_sha256_%d",
+       cert_depth);
+    setenv_str (es, envname,
+       format_hex_ex(BPTR(&sha256), BLEN(&sha256), 0, 1, ":", &gc));
   }

   /* export serial number as environmental variable */
@@ -638,8 +628,8 @@ verify_cert(struct tls_session *session, 
openvpn_x509_cert_t *cert, int cert_dep
   /* verify level 1 cert, i.e. the CA that signed our leaf cert */
   if (cert_depth == 1 && opt->verify_hash)
     {
-      unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc);
-      if (memcmp (sha1_hash, opt->verify_hash, SHA_DIGEST_LENGTH))
+      struct buffer sha1_hash = x509_get_sha1_fingerprint(cert, &gc);
+      if (memcmp (BPTR (&sha1_hash), opt->verify_hash, BLEN(&sha1_hash)))
       {
        msg (D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification 
failed");
        goto cleanup;
diff --git a/src/openvpn/ssl_verify.h b/src/openvpn/ssl_verify.h
index f693b2a..e5b5950 100644
--- a/src/openvpn/ssl_verify.h
+++ b/src/openvpn/ssl_verify.h
@@ -55,7 +55,7 @@

 /** Structure containing the hash for a single certificate */
 struct cert_hash {
-  unsigned char sha1_hash[SHA_DIGEST_LENGTH]; /**< The SHA1 hash for a 
certificate */
+  unsigned char sha256_hash[256/8];
 };

 /** Structure containing the hashes for a full certificate chain */
diff --git a/src/openvpn/ssl_verify_backend.h b/src/openvpn/ssl_verify_backend.h
index 9d2057b..91e6ec9 100644
--- a/src/openvpn/ssl_verify_backend.h
+++ b/src/openvpn/ssl_verify_backend.h
@@ -66,10 +66,10 @@ result_t verify_cert(struct tls_session *session, 
openvpn_x509_cert_t *cert, int
  *
  * @param session      TLS Session associated with this tunnel
  * @param cert_depth   Depth of the current certificate
- * @param sha1_hash    Hash of the current certificate
+ * @param cert_hash    Hash of the current certificate
  */
 void cert_hash_remember (struct tls_session *session, const int cert_depth,
-    const unsigned char *sha1_hash);
+    const struct buffer *cert_hash);

 /*
  * Library-specific functions.
@@ -87,14 +87,27 @@ void cert_hash_remember (struct tls_session *session, const 
int cert_depth,
  */
 char *x509_get_subject (openvpn_x509_cert_t *cert, struct gc_arena *gc);

-/* Retrieve the certificate's SHA1 hash.
+/**
+ * Retrieve the certificate's SHA1 fingerprint.
  *
- * @param cert         Certificate to retrieve the hash from.
+ * @param cert         Certificate to retrieve the fingerprint from.
  * @param gc           Garbage collection arena to use when allocating string.
  *
- * @return             a string containing the SHA1 hash of the certificate
+ * @return             a string containing the certificate fingerprint
  */
-unsigned char *x509_get_sha1_hash (openvpn_x509_cert_t *cert, struct gc_arena 
*gc);
+struct buffer x509_get_sha1_fingerprint (openvpn_x509_cert_t *cert,
+    struct gc_arena *gc);
+
+/**
+ * Retrieve the certificate's SHA256 fingerprint.
+ *
+ * @param cert         Certificate to retrieve the fingerprint from.
+ * @param gc           Garbage collection arena to use when allocating string.
+ *
+ * @return             a string containing the certificate fingerprint
+ */
+struct buffer x509_get_sha256_fingerprint (openvpn_x509_cert_t *cert,
+    struct gc_arena *gc);

 /*
  * Retrieve the certificate's username from the specified field.
diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c
index 9c4b51a..d018938 100644
--- a/src/openvpn/ssl_verify_mbedtls.c
+++ b/src/openvpn/ssl_verify_mbedtls.c
@@ -60,7 +60,8 @@ verify_callback (void *session_obj, mbedtls_x509_crt *cert, 
int cert_depth,
   session->verified = false;

   /* Remember certificate hash */
-  cert_hash_remember (session, cert_depth, x509_get_sha1_hash(cert, &gc));
+  struct buffer cert_fingerprint = x509_get_sha256_fingerprint (cert, &gc);
+  cert_hash_remember (session, cert_depth, &cert_fingerprint);

   /* did peer present cert which was signed by our root cert? */
   if (*flags != 0)
@@ -193,12 +194,29 @@ backend_x509_get_serial_hex (mbedtls_x509_crt *cert, 
struct gc_arena *gc)
   return buf;
 }

-unsigned char *
-x509_get_sha1_hash (mbedtls_x509_crt *cert, struct gc_arena *gc)
+static struct buffer
+x509_get_fingerprint (const mbedtls_md_info_t *md_info, mbedtls_x509_crt *cert,
+    struct gc_arena *gc)
 {
-  unsigned char *sha1_hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc);
-  mbedtls_sha1(cert->raw.p, cert->tbs.len, sha1_hash);
-  return sha1_hash;
+  const size_t md_size = mbedtls_md_get_size (md_info);
+  struct buffer fingerprint = alloc_buf_gc (md_size, gc);
+  mbedtls_md(md_info, cert->raw.p, cert->tbs.len, BPTR (&fingerprint));
+  ASSERT (buf_inc_len(&fingerprint, md_size));
+  return fingerprint;
+}
+
+struct buffer
+x509_get_sha1_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc)
+{
+  return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA1),
+      cert, gc);
+}
+
+struct buffer
+x509_get_sha256_fingerprint (mbedtls_x509_crt *cert, struct gc_arena *gc)
+{
+  return x509_get_fingerprint(mbedtls_md_info_from_type(MBEDTLS_MD_SHA256),
+      cert, gc);
 }

 char *
@@ -291,12 +309,20 @@ x509_setenv_track (const struct x509_track *xt, struct 
env_set *es,
     {
       if (depth == 0 || (xt->flags & XT_FULL_CHAIN))
        {
-         if (0 == strcmp(xt->name, "SHA1"))
+         if (0 == strcmp(xt->name, "SHA1") || 0 == strcmp(xt->name, "SHA256"))
            {
-             /* SHA1 fingerprint is not part of X509 structure */
-             unsigned char *sha1_hash = x509_get_sha1_hash(cert, &gc);
-             char *sha1_fingerprint = format_hex_ex(sha1_hash, 
SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc);
-             do_setenv_x509(es, xt->name, sha1_fingerprint, depth);
+             /* Fingerprint is not part of X509 structure */
+             struct buffer cert_hash;
+             char *fingerprint;
+
+             if (0 == strcmp(xt->name, "SHA1"))
+               cert_hash = x509_get_sha1_fingerprint(cert, &gc);
+             else
+               cert_hash = x509_get_sha256_fingerprint(cert, &gc);
+
+             fingerprint = format_hex_ex(BPTR(&cert_hash),
+                 BLEN(&cert_hash), 0, 1 | FHE_CAPS, ":", &gc);
+             do_setenv_x509(es, xt->name, fingerprint, depth);
            }
          else
            {
diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c
index 5817a05..a4b9432 100644
--- a/src/openvpn/ssl_verify_openssl.c
+++ b/src/openvpn/ssl_verify_openssl.c
@@ -61,8 +61,8 @@ verify_callback (int preverify_ok, X509_STORE_CTX * ctx)
   session = (struct tls_session *) SSL_get_ex_data (ssl, mydata_index);
   ASSERT (session);

-  cert_hash_remember (session, ctx->error_depth,
-      x509_get_sha1_hash(ctx->current_cert, &gc));
+  struct buffer cert_hash = x509_get_sha256_fingerprint(ctx->current_cert, 
&gc);
+  cert_hash_remember (session, ctx->error_depth, &cert_hash);

   /* did peer present cert which was signed by our root cert? */
   if (!preverify_ok)
@@ -248,11 +248,21 @@ backend_x509_get_serial_hex (openvpn_x509_cert_t *cert, 
struct gc_arena *gc)
   return format_hex_ex(asn1_i->data, asn1_i->length, 0, 1, ":", gc);
 }

-unsigned char *
-x509_get_sha1_hash (X509 *cert, struct gc_arena *gc)
+struct buffer
+x509_get_sha1_fingerprint (X509 *cert, struct gc_arena *gc)
 {
-  unsigned char *hash = gc_malloc(SHA_DIGEST_LENGTH, false, gc);
-  memcpy(hash, cert->sha1_hash, SHA_DIGEST_LENGTH);
+  struct buffer hash = alloc_buf_gc(sizeof(cert->sha1_hash), gc);
+  memcpy(BPTR(&hash), cert->sha1_hash, sizeof(cert->sha1_hash));
+  ASSERT (buf_inc_len(&hash, sizeof (cert->sha1_hash)));
+  return hash;
+}
+
+struct buffer
+x509_get_sha256_fingerprint (X509 *cert, struct gc_arena *gc)
+{
+  struct buffer hash = alloc_buf_gc((EVP_sha256())->md_size, gc);
+  X509_digest(cert, EVP_sha256(), BPTR(&hash), NULL);
+  ASSERT (buf_inc_len(&hash, (EVP_sha256())->md_size));
   return hash;
 }

@@ -376,10 +386,19 @@ x509_setenv_track (const struct x509_track *xt, struct 
env_set *es, const int de
          switch (xt->nid)
            {
            case NID_sha1:
+           case NID_sha256:
              {
-               char *sha1_fingerprint = format_hex_ex(x509->sha1_hash,
-                                 SHA_DIGEST_LENGTH, 0, 1 | FHE_CAPS, ":", &gc);
-               do_setenv_x509(es, xt->name, sha1_fingerprint, depth);
+               struct buffer fp_buf;
+               char *fp_str = NULL;
+
+               if (xt->nid == NID_sha1)
+                 fp_buf = x509_get_sha1_fingerprint(x509, &gc);
+               else
+                 fp_buf = x509_get_sha256_fingerprint(x509, &gc);
+
+               fp_str = format_hex_ex(BPTR(&fp_buf), BLEN(&fp_buf), 0,
+                   1 | FHE_CAPS, ":", &gc);
+               do_setenv_x509(es, xt->name, fp_str, depth);
              }
              break;
            default:
-- 
2.7.4


Reply via email to