This enhances --verify-hash with an optional algorithm flag.  If not
provided, it defaults to SHA1 to preserve backwards compatbilitity with
existing configurations.  The only valid flags are SHA1 and SHA256.

In addition it moves the hash verification away from memcmp() to
memcmp_constant_time().  And slightly it enhances the layout of the
--verify-hash section in the man page.

Signed-off-by: David Sommerseth <dav...@openvpn.net>

---

Simple test script
==================

* Server:
  # openvpn --dev tun --reneg-sec 60 --verb 4 \
            --server 10.8.0.0 255.255.255.0 --topology subnet \
            --ca sample/sample-keys/ca.crt \
            --key sample/sample-keys/server.key \
            --cert sample/sample-keys/server.crt \
            --dh sample/sample-keys/dh2048.pem \
            --cipher AES-256-CBC --auth SHA256 \
            --verify-hash 
57:4D:B3:49:7E:FB:9E:FD:DC:BC:A4:ED:BC:B3:C2:8D:C3:D7:2A:E3:0F:6F:76:57:F0:BB:F4:90:56:1C:DC:F9
 SHA256

* Client:
  # openvpn --dev tun --verb 4 \
            --remote 192.168.122.1 --explicit-exit-notify \
            --client --ca sample/sample-keys/ca.crt \
            --key sample/sample-keys/client.key \
            --cert sample/sample-keys/client.crt \
            --cipher AES-256-CBC --auth SHA256 \
            --verify-hash 
06:30:DA:EA:77:41:5B:D6:8C:C5:CC:CA:F5:D7:91:87:FA:6D:89:44

  Alternative --verify-hash variant which must succeed:
     --verify-hash 06:30:DA:EA:77:41:5B:D6:8C:C5:CC:CA:F5:D7:91:87:FA:6D:89:44 
SHA1

  These --verify-hash variants must fail:
     --verify-hash 06:30:DA:EA:77:41:5B:D6:8C:C5:CC:CA:F5:D7:91:87:FA:6D:89:44 
SHA256
     --verify-hash 
57:4D:B3:49:7E:FB:9E:FD:DC:BC:A4:ED:BC:B3:C2:8D:C3:D7:2A:E3:0F:6F:76:57:F0:BB:F4:90:56:1C:DC:F9
     --verify-hash 
57:4D:B3:49:7E:FB:9E:FD:DC:BC:A4:ED:BC:B3:C2:8D:C3:D7:2A:E3:0F:6F:76:57:F0:BB:F4:90:56:1C:DC:00
 SHA256
     --verify-hash 06:30:DA:EA:77:41:5B:D6:8C:C5:CC:CA:F5:D7:91:87:FA:6D:89:00 
SHA1
     --verify-hash 06:30:DA:EA:77:41:5B:D6:8C:C5:CC:CA:F5:D7:91:87:FA:6D:89:00
     --verify-hash 06:30:DA:EA:77:41:5B:D6:8C:C5:CC:CA:F5:D7:91:87:FA:6D:89:XX

To retrieve the SHA fingerprints in certificates:

    $ openssl x509 -noout -fingerprint -in sample/sample-keys/ca.crt
    $ openssl x509 -noout -fingerprint -sha256 -in sample/sample-keys/ca.crt
---
 Changes.rst                  |  3 +++
 doc/openvpn.8                | 18 +++++++++++++++---
 src/openvpn/crypto_backend.h |  6 ++++++
 src/openvpn/init.c           |  1 +
 src/openvpn/options.c        | 22 +++++++++++++++++++---
 src/openvpn/options.h        |  5 +++++
 src/openvpn/ssl_common.h     |  1 +
 src/openvpn/ssl_verify.c     | 15 +++++++++++++--
 8 files changed, 63 insertions(+), 8 deletions(-)

diff --git a/Changes.rst b/Changes.rst
index 2a94990e..da93060c 100644
--- a/Changes.rst
+++ b/Changes.rst
@@ -318,3 +318,6 @@ Version 2.4.1
    ``--remote-cert-tls`` uses the far more common keyUsage and extendedKeyUsage
    extension instead.  Make sure your certificates carry these to be able to
    use ``--remote-cert-tls``.
+ - ``--verify-hash`` can now take an optional flag which changes the hashing
+   algorithm. It can be either SHA1 or SHA256.  The default if not provided is
+   SHA1 to preserve backwards compatibility with existing configurations.
diff --git a/doc/openvpn.8 b/doc/openvpn.8
index c3248fde..fcf825c6 100644
--- a/doc/openvpn.8
+++ b/doc/openvpn.8
@@ -4694,15 +4694,27 @@ and
 Not available with PolarSSL.
 .\"*********************************************************
 .TP
-.B \-\-verify\-hash hash
-Specify SHA1 fingerprint for level-1 cert.  The level-1 cert is the
+.B \-\-verify\-hash hash [algo]
+Specify SHA1 or SHA256 fingerprint for level-1 cert.  The level-1 cert is the
 CA (or intermediate cert) that signs the leaf certificate, and is
 one removed from the leaf certificate in the direction of the root.
 When accepting a connection from a peer, the level-1 cert
 fingerprint must match
 .B hash
 or certificate verification will fail.  Hash is specified
-as XX:XX:...  For example: 
AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
+as XX:XX:... For example:
+
+.nf
+.ft 3
+.in +4
+AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
+.in -4
+.ft
+.fi
+
+The
+.B algo
+flag can be either SHA1 or SHA256.  If not provided, it defaults to SHA1.
 .\"*********************************************************
 .TP
 .B \-\-pkcs11\-cert\-private [0|1]...
diff --git a/src/openvpn/crypto_backend.h b/src/openvpn/crypto_backend.h
index 2c79baa1..9b113d7b 100644
--- a/src/openvpn/crypto_backend.h
+++ b/src/openvpn/crypto_backend.h
@@ -47,6 +47,12 @@
 /* Maximum HMAC digest size (bytes) */
 #define OPENVPN_MAX_HMAC_SIZE   64
 
+/** Types referencing specific message digest hashing algorithms */
+typedef enum {
+    MD_SHA1,
+    MD_SHA256
+} hash_algo_type ;
+
 /** Struct used in cipher name translation table */
 typedef struct {
     const char *openvpn_name;   /**< Cipher name used by OpenVPN */
diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index ee14f672..e9455039 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2619,6 +2619,7 @@ do_init_crypto_tls(struct context *c, const unsigned int 
flags)
     memmove(to.remote_cert_ku, options->remote_cert_ku, 
sizeof(to.remote_cert_ku));
     to.remote_cert_eku = options->remote_cert_eku;
     to.verify_hash = options->verify_hash;
+    to.verify_hash_algo = options->verify_hash_algo;
 #ifdef ENABLE_X509ALTUSERNAME
     to.x509_username_field = (char *) options->x509_username_field;
 #else
diff --git a/src/openvpn/options.c b/src/openvpn/options.c
index dcb6ecfa..f38abad7 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -591,7 +591,8 @@ static const char usage_message[] =
     "--x509-username-field : Field in x509 certificate containing the 
username.\n"
     "                        Default is CN in the Subject field.\n"
 #endif
-    "--verify-hash   : Specify SHA1 fingerprint for level-1 cert.\n"
+    "--verify-hash hash [algo] : Specify fingerprint for level-1 
certificate.\n"
+    "                            Valid algo flags are SHA1 and SHA256. \n"
 #ifdef _WIN32
     "--cryptoapicert select-string : Load the certificate and private key from 
the\n"
     "                  Windows Certificate System Store.\n"
@@ -7687,10 +7688,25 @@ add_option(struct options *options,
             options->extra_certs_file_inline = p[2];
         }
     }
-    else if (streq(p[0], "verify-hash") && p[1] && !p[2])
+    else if (streq(p[0], "verify-hash") && p[1] && !p[3])
     {
         VERIFY_PERMISSION(OPT_P_GENERAL);
-        options->verify_hash = parse_hash_fingerprint(p[1], SHA_DIGEST_LENGTH, 
msglevel, &options->gc);
+
+        if (!p[2] || (p[2] && streq(p[2], "SHA1")))
+        {
+            options->verify_hash = parse_hash_fingerprint(p[1], 
SHA_DIGEST_LENGTH, msglevel, &options->gc);
+            options->verify_hash_algo = MD_SHA1;
+        }
+        else if (p[2] && streq(p[2], "SHA256"))
+        {
+            options->verify_hash = parse_hash_fingerprint(p[1], 
SHA256_DIGEST_LENGTH, msglevel, &options->gc);
+            options->verify_hash_algo = MD_SHA256;
+        }
+        else
+        {
+            msg(msglevel, "invalid or unsupported hashing algorithm: %s  (only 
SHA1 and SHA256 are valid)", p[2]);
+            goto err;
+        }
     }
 #ifdef ENABLE_CRYPTOAPI
     else if (streq(p[0], "cryptoapicert") && p[1] && !p[2])
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index dc63aeda..5ad087e0 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -42,6 +42,10 @@
 #include "comp.h"
 #include "pushlist.h"
 #include "clinat.h"
+#ifdef ENABLE_CRYPTO
+#include "crypto_backend.h"
+#endif
+
 
 /*
  * Maximum number of parameters associated with an option,
@@ -518,6 +522,7 @@ struct options
     unsigned remote_cert_ku[MAX_PARMS];
     const char *remote_cert_eku;
     uint8_t *verify_hash;
+    hash_algo_type verify_hash_algo;
     unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */
 
 #ifdef ENABLE_PKCS11
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 9a16d77e..a99d24d6 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -271,6 +271,7 @@ struct tls_options
     unsigned remote_cert_ku[MAX_PARMS];
     const char *remote_cert_eku;
     uint8_t *verify_hash;
+    hash_algo_type verify_hash_algo;
     char *x509_username_field;
 
     /* allow openvpn config info to be
diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c
index ac1e110a..b4dbaf9b 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -718,8 +718,19 @@ 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)
     {
-        struct buffer sha1_hash = x509_get_sha1_fingerprint(cert, &gc);
-        if (memcmp(BPTR(&sha1_hash), opt->verify_hash, BLEN(&sha1_hash)))
+        struct buffer ca_hash;
+
+        switch (opt->verify_hash_algo)
+        {
+        case MD_SHA1:
+            ca_hash = x509_get_sha1_fingerprint(cert, &gc);
+            break;
+
+        case MD_SHA256:
+            ca_hash = x509_get_sha256_fingerprint(cert, &gc);
+            break;
+        }
+        if (memcmp_constant_time(BPTR(&ca_hash), opt->verify_hash, 
BLEN(&ca_hash)))
         {
             msg(D_TLS_ERRORS, "TLS Error: level-1 certificate hash 
verification failed");
             goto cleanup;
-- 
2.11.0


------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to