Browse code

crypto: Enable SHA256 fingerprint checking in --verify-hash

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 enhance the layout of the --verify-hash section in the man
page.

Signed-off-by: David Sommerseth <davids@openvpn.net>
Acked-by: Steffan Karger <steffan.karger@fox-it.com>
Message-Id: <20170504204201.1257-1-davids@openvpn.net>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg14538.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>

David Sommerseth authored on 2017/05/05 05:42:01
Showing 8 changed files
... ...
@@ -303,6 +303,11 @@ Maintainer-visible changes
303 303
   use -std=gnu99 in CFLAGS.  This is known to be needed when doing
304 304
   i386/i686 builds on RHEL5.
305 305
 
306
+Version 2.4.3
307
+=============
308
+- ``--verify-hash`` can now take an optional flag which changes the hashing
309
+  algorithm. It can be either SHA1 or SHA256.  The default if not provided is
310
+  SHA1 to preserve backwards compatibility with existing configurations.
306 311
 
307 312
 Version 2.4.1
308 313
 =============
... ...
@@ -4694,15 +4694,27 @@ and
4694 4694
 Not available with PolarSSL.
4695 4695
 .\"*********************************************************
4696 4696
 .TP
4697
-.B \-\-verify\-hash hash
4698
-Specify SHA1 fingerprint for level-1 cert.  The level-1 cert is the
4697
+.B \-\-verify\-hash hash [algo]
4698
+Specify SHA1 or SHA256 fingerprint for level-1 cert.  The level-1 cert is the
4699 4699
 CA (or intermediate cert) that signs the leaf certificate, and is
4700 4700
 one removed from the leaf certificate in the direction of the root.
4701 4701
 When accepting a connection from a peer, the level-1 cert
4702 4702
 fingerprint must match
4703 4703
 .B hash
4704 4704
 or certificate verification will fail.  Hash is specified
4705
-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
4705
+as XX:XX:... For example:
4706
+
4707
+.nf
4708
+.ft 3
4709
+.in +4
4710
+AD:B0:95:D8:09:C8:36:45:12:A9:89:C8:90:09:CB:13:72:A6:AD:16
4711
+.in -4
4712
+.ft
4713
+.fi
4714
+
4715
+The
4716
+.B algo
4717
+flag can be either SHA1 or SHA256.  If not provided, it defaults to SHA1.
4706 4718
 .\"*********************************************************
4707 4719
 .TP
4708 4720
 .B \-\-pkcs11\-cert\-private [0|1]...
... ...
@@ -47,6 +47,12 @@
47 47
 /* Maximum HMAC digest size (bytes) */
48 48
 #define OPENVPN_MAX_HMAC_SIZE   64
49 49
 
50
+/** Types referencing specific message digest hashing algorithms */
51
+typedef enum {
52
+    MD_SHA1,
53
+    MD_SHA256
54
+} hash_algo_type ;
55
+
50 56
 /** Struct used in cipher name translation table */
51 57
 typedef struct {
52 58
     const char *openvpn_name;   /**< Cipher name used by OpenVPN */
... ...
@@ -2619,6 +2619,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
2619 2619
     memmove(to.remote_cert_ku, options->remote_cert_ku, sizeof(to.remote_cert_ku));
2620 2620
     to.remote_cert_eku = options->remote_cert_eku;
2621 2621
     to.verify_hash = options->verify_hash;
2622
+    to.verify_hash_algo = options->verify_hash_algo;
2622 2623
 #ifdef ENABLE_X509ALTUSERNAME
2623 2624
     to.x509_username_field = (char *) options->x509_username_field;
2624 2625
 #else
... ...
@@ -591,7 +591,8 @@ static const char usage_message[] =
591 591
     "--x509-username-field : Field in x509 certificate containing the username.\n"
592 592
     "                        Default is CN in the Subject field.\n"
593 593
 #endif
594
-    "--verify-hash   : Specify SHA1 fingerprint for level-1 cert.\n"
594
+    "--verify-hash hash [algo] : Specify fingerprint for level-1 certificate.\n"
595
+    "                            Valid algo flags are SHA1 and SHA256. \n"
595 596
 #ifdef _WIN32
596 597
     "--cryptoapicert select-string : Load the certificate and private key from the\n"
597 598
     "                  Windows Certificate System Store.\n"
... ...
@@ -7687,10 +7688,25 @@ add_option(struct options *options,
7687 7687
             options->extra_certs_file_inline = p[2];
7688 7688
         }
7689 7689
     }
7690
-    else if (streq(p[0], "verify-hash") && p[1] && !p[2])
7690
+    else if (streq(p[0], "verify-hash") && p[1] && !p[3])
7691 7691
     {
7692 7692
         VERIFY_PERMISSION(OPT_P_GENERAL);
7693
-        options->verify_hash = parse_hash_fingerprint(p[1], SHA_DIGEST_LENGTH, msglevel, &options->gc);
7693
+
7694
+        if (!p[2] || (p[2] && streq(p[2], "SHA1")))
7695
+        {
7696
+            options->verify_hash = parse_hash_fingerprint(p[1], SHA_DIGEST_LENGTH, msglevel, &options->gc);
7697
+            options->verify_hash_algo = MD_SHA1;
7698
+        }
7699
+        else if (p[2] && streq(p[2], "SHA256"))
7700
+        {
7701
+            options->verify_hash = parse_hash_fingerprint(p[1], SHA256_DIGEST_LENGTH, msglevel, &options->gc);
7702
+            options->verify_hash_algo = MD_SHA256;
7703
+        }
7704
+        else
7705
+        {
7706
+            msg(msglevel, "invalid or unsupported hashing algorithm: %s  (only SHA1 and SHA256 are valid)", p[2]);
7707
+            goto err;
7708
+        }
7694 7709
     }
7695 7710
 #ifdef ENABLE_CRYPTOAPI
7696 7711
     else if (streq(p[0], "cryptoapicert") && p[1] && !p[2])
... ...
@@ -42,6 +42,10 @@
42 42
 #include "comp.h"
43 43
 #include "pushlist.h"
44 44
 #include "clinat.h"
45
+#ifdef ENABLE_CRYPTO
46
+#include "crypto_backend.h"
47
+#endif
48
+
45 49
 
46 50
 /*
47 51
  * Maximum number of parameters associated with an option,
... ...
@@ -518,6 +522,7 @@ struct options
518 518
     unsigned remote_cert_ku[MAX_PARMS];
519 519
     const char *remote_cert_eku;
520 520
     uint8_t *verify_hash;
521
+    hash_algo_type verify_hash_algo;
521 522
     unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */
522 523
 
523 524
 #ifdef ENABLE_PKCS11
... ...
@@ -271,6 +271,7 @@ struct tls_options
271 271
     unsigned remote_cert_ku[MAX_PARMS];
272 272
     const char *remote_cert_eku;
273 273
     uint8_t *verify_hash;
274
+    hash_algo_type verify_hash_algo;
274 275
     char *x509_username_field;
275 276
 
276 277
     /* allow openvpn config info to be
... ...
@@ -718,8 +718,31 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
718 718
     /* verify level 1 cert, i.e. the CA that signed our leaf cert */
719 719
     if (cert_depth == 1 && opt->verify_hash)
720 720
     {
721
-        struct buffer sha1_hash = x509_get_sha1_fingerprint(cert, &gc);
722
-        if (memcmp(BPTR(&sha1_hash), opt->verify_hash, BLEN(&sha1_hash)))
721
+        struct buffer ca_hash = {0};
722
+
723
+        switch (opt->verify_hash_algo)
724
+        {
725
+        case MD_SHA1:
726
+            ca_hash = x509_get_sha1_fingerprint(cert, &gc);
727
+            break;
728
+
729
+        case MD_SHA256:
730
+            ca_hash = x509_get_sha256_fingerprint(cert, &gc);
731
+            break;
732
+
733
+        default:
734
+            /* This should normally not happen at all; the algorithm used
735
+             * is parsed by add_option() [options.c] and set to a predefined
736
+             * value in an enumerated type.  So if this unlikely scenario
737
+             * happens, consider this a failure
738
+             */
739
+            msg(M_WARN, "Unexpected invalid algorithm used with "
740
+                "--verify-hash (%i)", opt->verify_hash_algo);
741
+            ret = FAILURE;
742
+            goto cleanup;
743
+        }
744
+
745
+        if (memcmp(BPTR(&ca_hash), opt->verify_hash, BLEN(&ca_hash)))
723 746
         {
724 747
             msg(D_TLS_ERRORS, "TLS Error: level-1 certificate hash verification failed");
725 748
             goto cleanup;