]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Implement peer-fingerprint to check fingerprint of peer certificate
authorArne Schwabe <arne@rfc2549.org>
Sun, 21 Mar 2021 14:33:53 +0000 (15:33 +0100)
committerGert Doering <gert@greenie.muc.de>
Sun, 21 Mar 2021 18:45:43 +0000 (19:45 +0100)
This option allows to pin one or more more peer certificates. It also
prepares for doing TLS authentication without a CA and just
self-signed certificates.

Patch V2: Allow peer-fingerprint to be specified multiple times
          to allow multiple peers without needing to use inline
          syntax. (e.g. on command line).

Patch V3: rebase on v3 of 1/4, reword message of verify-hash and
          peer-fingerpring incompatibility

Signed-off-by: Arne Schwabe <arne@rfc2549.org>
Acked-by: Antonio Quartulli <antonio@openvpn.net>
Message-Id: <20210321143353.2677-1-arne@rfc2549.org>
URL: https://www.mail-archive.com/search?l=mid&q=20210321143353.2677-1-arne@rfc2549.org
Signed-off-by: Gert Doering <gert@greenie.muc.de>
Changes.rst
doc/man-sections/inline-files.rst
doc/man-sections/tls-options.rst
src/openvpn/init.c
src/openvpn/options.c
src/openvpn/options.h
src/openvpn/ssl_common.h
src/openvpn/ssl_verify.c

index d6be10502e20df1edc34684899e7aa872a9748e5..c0e0d5d9786f5437da3c6ee65c15c77265e03d01 100644 (file)
@@ -19,6 +19,12 @@ Compatibility with OpenSSL in FIPS mode
     and if less than 100MB RAM are available, use setrlimit() to upgrade
     the limit.  See Trac #1390.  Not available on OpenSolaris.
 
+Certificate pinning/verify peer fingerprint
+    The ``--peer-fingerprint`` option has been introduced to give users an
+    easy to use alternative to the ``tls-verify`` for matching the
+    fingerprint of the peer. The option takes use a number of allowed
+    SHA256 certificate fingerprints.
+
 
 Deprecated features
 -------------------
index 303bb3c8a1a6505ba2cfe77d00a61dc736f4f5cc..01e4a8406044958cda6fbbe4f0beffeda29bbe49 100644 (file)
@@ -4,8 +4,8 @@ INLINE FILE SUPPORT
 OpenVPN allows including files in the main configuration for the ``--ca``,
 ``--cert``, ``--dh``, ``--extra-certs``, ``--key``, ``--pkcs12``,
 ``--secret``, ``--crl-verify``, ``--http-proxy-user-pass``, ``--tls-auth``,
-``--auth-gen-token-secret``, ``--tls-crypt``, ``--tls-crypt-v2`` and
-``--verify-hash`` options.
+``--auth-gen-token-secret``, ``--peer-fingerprint``, ``--tls-crypt``,
+``--tls-crypt-v2`` and ``--verify-hash`` options.
 
 Each inline file started by the line ``<option>`` and ended by the line
 ``</option>``
index d8f9800e76aa72539f27680d7dc37e140e298bde..cfe1ec98adee0b0e200a28c4ff20600de2a16490 100644 (file)
@@ -271,7 +271,8 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa
   man-in-the-middle attack where an authorized client attempts to connect
   to another client by impersonating the server. The attack is easily
   prevented by having clients verify the server certificate using any one
-  of ``--remote-cert-tls``, ``--verify-x509-name``, or ``--tls-verify``.
+  of ``--remote-cert-tls``, ``--verify-x509-name``, ``--peer-fingerprint``
+  or ``--tls-verify``.
 
 --tls-auth args
   Add an additional layer of HMAC authentication on top of the TLS control
@@ -592,6 +593,25 @@ certificates and keys: https://github.com/OpenVPN/easy-rsa
 
 If the option is inlined, ``algo`` is always :code:`SHA256`.
 
+--peer-fingerprint args
+   Specify a SHA256 fingerprint or list of SHA256 fingerprints to verify
+   the peer certificate against. The peer certificate must match one of the
+   fingerprint or certificate verification will fail. The option can also
+   be inlined
+
+  Valid syntax:
+  ::
+
+    peer-fingerprint AD:B0:95:D8:09:...
+
+  or inline:
+  ::
+
+    <peer-fingerprint>
+    00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff
+    11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff:00
+    </peer-fingerprint>
+
 --verify-x509-name args
   Accept connections only if a host's X.509 name is equal to **name.** The
   remote host must also pass all other tests of verification.
index d234729c651ff1d6640929c87d158b44d746efd7..731b0cf2b4f5aa69a0dd70578ba3c01e2837862b 100644 (file)
@@ -2927,6 +2927,7 @@ do_init_crypto_tls(struct context *c, const unsigned int flags)
     to.remote_cert_eku = options->remote_cert_eku;
     to.verify_hash = options->verify_hash;
     to.verify_hash_algo = options->verify_hash_algo;
+    to.verify_hash_depth = options->verify_hash_depth;
 #ifdef ENABLE_X509ALTUSERNAME
     memcpy(to.x509_username_field, options->x509_username_field, sizeof(to.x509_username_field));
 #else
index d1ff4e089687c32a6796f33b722d235a357c58ed..38ced536d2d829a023ee8d04acd2db41d56d9c18 100644 (file)
@@ -8117,26 +8117,45 @@ add_option(struct options *options,
         options->extra_certs_file = p[1];
         options->extra_certs_file_inline = is_inline;
     }
-    else if (streq(p[0], "verify-hash") && p[1] && !p[3])
+    else if ((streq(p[0], "verify-hash") && p[1] && !p[3])
+             || (streq(p[0], "peer-fingerprint") && p[1] && !p[2]))
     {
         VERIFY_PERMISSION(OPT_P_GENERAL|OPT_P_INLINE);
+
+        int verify_hash_depth = 0;
+        if (streq(p[0], "verify-hash"))
+        {
+            /* verify level 1 cert, i.e. the CA that signed the leaf cert */
+            verify_hash_depth = 1;
+        }
+
         options->verify_hash_algo = MD_SHA256;
 
         int digest_len = SHA256_DIGEST_LENGTH;
 
-        if ((!p[2] && !is_inline) || (p[2] && streq(p[2], "SHA1")))
+        if (options->verify_hash && options->verify_hash_depth != verify_hash_depth)
         {
-            options->verify_hash_algo = MD_SHA1;
-            msg(M_WARN, "DEPRECATED FEATURE: Usage of SHA1 fingerprints for "
-                "verify-hash is deprecated. You should switch to SHA256.");
-            options->verify_hash_algo = MD_SHA1;
-            digest_len = SHA_DIGEST_LENGTH;
+            msg(msglevel, "ERROR: Setting %s not allowed. --verify-hash and"
+                          " --peer-fingerprint are mutually exclusive", p[0]);
+            goto err;
         }
-        else if (p[2] && !streq(p[2], "SHA256"))
+
+        if (streq(p[0], "verify-hash"))
         {
-            msg(msglevel, "invalid or unsupported hashing algorithm: %s "
-                          "(only SHA1 and SHA256 are valid)", p[2]);
-            goto err;
+            if ((!p[2] && !is_inline) || (p[2] && streq(p[2], "SHA1")))
+            {
+                options->verify_hash_algo = MD_SHA1;
+                msg(M_WARN, "DEPRECATED FEATURE: Usage of SHA1 fingerprints for "
+                    "verify-hash is deprecated. You should switch to SHA256.");
+                options->verify_hash_algo = SHA_DIGEST_LENGTH;
+                digest_len = SHA_DIGEST_LENGTH;
+            }
+            else if (p[2] && !streq(p[2], "SHA256"))
+            {
+                msg(msglevel, "invalid or unsupported hashing algorithm: %s "
+                              "(only SHA1 and SHA256 are supported)", p[2]);
+                goto err;
+            }
         }
 
         struct verify_hash_list *newlist;
@@ -8147,6 +8166,7 @@ add_option(struct options *options,
         if (!options->verify_hash)
         {
             options->verify_hash = newlist;
+            options->verify_hash_depth = verify_hash_depth;
         }
         else
         {
index a7b3174f5eb4f65a01b5383aa5286b82d2374489..30ec53d67449896484293deeb1d93d9d40635e46 100644 (file)
@@ -560,6 +560,7 @@ struct options
     const char *remote_cert_eku;
     struct verify_hash_list *verify_hash;
     hash_algo_type verify_hash_algo;
+    int verify_hash_depth;
     unsigned int ssl_flags; /* set to SSLF_x flags from ssl.h */
 
 #ifdef ENABLE_PKCS11
index f6aaae98ff18cd9820eec7ba366f081e2734e6ce..2b1b87fb8f2f3d75eab97e8c81f160718cb87dc5 100644 (file)
@@ -284,6 +284,7 @@ struct tls_options
     unsigned remote_cert_ku[MAX_PARMS];
     const char *remote_cert_eku;
     struct verify_hash_list *verify_hash;
+    int verify_hash_depth;
     hash_algo_type verify_hash_algo;
 #ifdef ENABLE_X509ALTUSERNAME
     char *x509_username_field[MAX_PARMS];
index 06de0f5fff14650030378f17809473ebef55370a..923eac9194323caec9f7cddbfe4e2a04f7511142 100644 (file)
@@ -721,19 +721,18 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
         goto cleanup;                   /* Reject connection */
     }
 
-    /* verify level 1 cert, i.e. the CA that signed our leaf cert */
-    if (cert_depth == 1 && opt->verify_hash)
+    if (cert_depth == opt->verify_hash_depth && opt->verify_hash)
     {
-        struct buffer ca_hash = {0};
+        struct buffer cert_fp = {0};
 
         switch (opt->verify_hash_algo)
         {
             case MD_SHA1:
-                ca_hash = x509_get_sha1_fingerprint(cert, &gc);
+                cert_fp = x509_get_sha1_fingerprint(cert, &gc);
                 break;
 
             case MD_SHA256:
-                ca_hash = x509_get_sha256_fingerprint(cert, &gc);
+                cert_fp = x509_get_sha256_fingerprint(cert, &gc);
                 break;
 
             default:
@@ -752,8 +751,8 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
 
         while (current_hash)
         {
-            if (memcmp_constant_time(BPTR(&ca_hash), current_hash->hash,
-                                     BLEN(&ca_hash)) == 0)
+            if (memcmp_constant_time(BPTR(&cert_fp), current_hash->hash,
+                                     BLEN(&cert_fp)) == 0)
             {
                 break;
             }
@@ -762,7 +761,11 @@ verify_cert(struct tls_session *session, openvpn_x509_cert_t *cert, int cert_dep
 
         if (!current_hash)
         {
-            msg(D_TLS_ERRORS, "TLS Error: --tls-verify certificate hash verification failed");
+            const char *hex_fp = format_hex_ex(BPTR(&cert_fp), BLEN(&cert_fp),
+                                               0, 1, ":", &gc);
+            msg(D_TLS_ERRORS, "TLS Error: --tls-verify/--peer-fingerprint"
+                "certificate hash verification failed. (got "
+                "fingerprint: %s", hex_fp);
             goto cleanup;
         }
     }