]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Add SHA256 fingerprint support
authorSteffan Karger <steffan@karger.me>
Thu, 5 May 2016 20:14:07 +0000 (22:14 +0200)
committerDavid Sommerseth <davids@openvpn.net>
Sat, 17 Sep 2016 12:23:04 +0000 (15:23 +0300)
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

[ DS: This commit squashes in the clean-up cert_hash_remember scoping patch,
      as it is highly related and tied to this primary patch ]

Signed-off-by: Steffan Karger <steffan@karger.me>
Acked-by: David Sommerseth <davids@openvpn.net>
Message-Id: 1462479247-21854-1-git-send-email-steffan@karger.me
Message-Id: 1474055635-7427-1-git-send-email-steffan@karger.me
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg11859.html
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg12464.html
Signed-off-by: David Sommerseth <davids@openvpn.net>
doc/openvpn.8
src/openvpn/ssl_verify.c
src/openvpn/ssl_verify.h
src/openvpn/ssl_verify_backend.h
src/openvpn/ssl_verify_mbedtls.c
src/openvpn/ssl_verify_openssl.c

index 2f4263608d5ab06bdc13e88e65ee632b3194b009..163bdf4bad3400c74786fe2a22ab6630a1262ad6 100644 (file)
@@ -6443,9 +6443,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
index b373bed1adaa58fa04b177eb0e9a2f747a981085..d0c22b84ece1bdc80a925b55df15190f77932d7a 100644 (file)
@@ -191,40 +191,25 @@ 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)
     {
       if (!session->cert_hash_set)
-       ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set);
+       {
+         ALLOC_OBJ_CLEAR (session->cert_hash_set, struct cert_hash_set);
+       }
       if (!session->cert_hash_set->ch[error_depth])
-       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);
-      }
-    }
-}
-
-#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));
+         ALLOC_OBJ (session->cert_hash_set->ch[error_depth], struct cert_hash);
        }
+
+      struct cert_hash *ch = session->cert_hash_set->ch[error_depth];
+      ASSERT (sizeof (ch->sha256_hash) == BLEN (cert_hash));
+      memcpy (ch->sha256_hash, BPTR (cert_hash), sizeof (ch->sha256_hash));
     }
-  gc_free (&gc);
 }
-#endif
 
 void
 cert_hash_free (struct cert_hash_set *chs)
@@ -251,7 +236,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 +264,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 +403,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 +631,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;
index f693b2a48746e536d3116a617fb1a3538f691fbc..e5b5950a0f1245eab6b15de75e1e0d077078a893 100644 (file)
@@ -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 */
index 9d2057b558d872defb7c01d5b9d62201587b2694..91e6ec9300de20c4998216f86ad82d0a965cd4fc 100644 (file)
@@ -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.
index 522ff68c4af9b252a119ccc8ebae349146755d97..92b0804bb5c82f5be8cdfe5bf3dca35d0e826aef 100644 (file)
@@ -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)
@@ -196,12 +197,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 *
@@ -294,12 +312,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
            {
index 5817a05eeda5d33f749920287dd9468f2d2e5b3d..a4b94328b362347e39da9658f0cd98aa004ac710 100644 (file)
@@ -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: