OpenVPN traditionally works around CAs. However many TLS-based protocols also
allow an alternative simpler mode in which rather than verify certificates
against CAs, the certificate itself is hashed and compared against a
pre-known set of acceptable hashes. This is usually referred to as
"fingerprint verification". It's popular across SMTP servers, IRC servers,
XMPP servers, and even in the context of HTTP with pinning.

So, I'd like to propose an extremely simple and non-invasive way of
supporting this in OpenVPN, by re-using several features that already
basically support it. Namely, what I propose is:

   * Allow specifying 'none' to the --ca parameter, to specify that
     certificates should not be checked against a CA. Note that 'none'
     is already used in other similar options as a special placeholder.

   * When '--ca none' is in use, --verify-hash checks all depths instead
     of just level 1.

With these very simple changes, fingerprint authentication is easily achieved
via the --tls-verify script on the server and via --verify-hash on the client.

I've included some instructions on how to use all of this.

Server side:
============

Make self-signed cert:
$ openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout 
key.pem -out cert.pem -nodes -sha256 -days 3650 -subj '/CN=server'

Record our fingerprint in an environment variable for the client to use later:
$ server_fingerprint="$(openssl x509 -in cert.pem -noout -sha256 -fingerprint | 
sed 's/.*=//;s/\(.*\)/\L\1/')"

Start openvpn with tls verify script:
$ sudo openvpn --server 10.66.0.0 255.255.255.0 --dev tun --dh none --ca none 
--cert cert.pem --key key.pem --tls-verify $(readlink -f tls-verify.sh) 
--script-security 2

TLS Verify Script:
==================

#!/bin/sh
[ -n "$tls_digest_sha256_0" -a -e 
"/tmp/allowed-openvpn-fingerprints/$tls_digest_sha256_0" ]

Client side:
============

Make self-signed cert:
$ openssl req -x509 -newkey ec:<(openssl ecparam -name secp384r1) -keyout 
key.pem -out cert.pem -nodes -sha256 -days 3650 -subj '/CN=client'

"Tell" the server about our fingerprint:
$ mkdir -p /tmp/allowed-openvpn-fingerprints; touch 
"/tmp/allowed-openvpn-fingerprints/$(openssl x509 -in cert.pem -noout -sha256 
-fingerprint | sed 's/.*=//;s/\(.*\)/\L\1/')"

Start openvpn with server fingerprint verification:
$ sudo openvpn --client --remote 127.0.0.1 --dev tun --ca none --cert cert.pem 
--key key.pem --verify-hash "$server_fingerprint" SHA256 --nobind

Signed-off-by: Jason A. Donenfeld <ja...@zx2c4.com>
---
Sorry for the double post. Somebody suggested I submit this as an actual
git patch instead of as just part of the goofy email I sent earlier. So
here's that email turned into a proper patch that you can apply. Feel
free to hack the patch to pieces or discard it and start over.

 src/openvpn/init.c               | 1 +
 src/openvpn/options.c            | 9 ++++++++-
 src/openvpn/options.h            | 1 +
 src/openvpn/ssl.c                | 2 +-
 src/openvpn/ssl_common.h         | 1 +
 src/openvpn/ssl_verify.c         | 2 +-
 src/openvpn/ssl_verify_mbedtls.c | 2 +-
 src/openvpn/ssl_verify_openssl.c | 2 +-
 8 files changed, 15 insertions(+), 5 deletions(-)

diff --git a/src/openvpn/init.c b/src/openvpn/init.c
index 36c1a4c4..4da994d8 100644
--- a/src/openvpn/init.c
+++ b/src/openvpn/init.c
@@ -2722,6 +2722,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.ca_file_none = options->ca_file_none;
 #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 426057ab..dad706d9 100644
--- a/src/openvpn/options.c
+++ b/src/openvpn/options.c
@@ -3259,7 +3259,10 @@ options_postprocess_filechecks(struct options *options)
 
     /* ** SSL/TLS/crypto related files ** */
     errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->dh_file, 
R_OK, "--dh");
-    errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->ca_file, 
R_OK, "--ca");
+    if (!options->ca_file_none)
+    {
+        errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->ca_file, 
R_OK, "--ca");
+    }
     errs |= check_file_access_chroot(options->chroot_dir, CHKACC_FILE, 
options->ca_path, R_OK, "--capath");
     errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, options->cert_file, 
R_OK, "--cert");
     errs |= check_file_access(CHKACC_FILE|CHKACC_INLINE, 
options->extra_certs_file, R_OK,
@@ -7638,6 +7641,10 @@ add_option(struct options *options,
         {
             options->ca_file_inline = p[2];
         }
+        else if (streq(p[1], "none"))
+        {
+            options->ca_file_none = true;
+        }
     }
 #ifndef ENABLE_CRYPTO_MBEDTLS
     else if (streq(p[0], "capath") && p[1] && !p[2])
diff --git a/src/openvpn/options.h b/src/openvpn/options.h
index f7d0145a..6ccdca49 100644
--- a/src/openvpn/options.h
+++ b/src/openvpn/options.h
@@ -489,6 +489,7 @@ struct options
     /* TLS (control channel) parms */
     bool tls_server;
     bool tls_client;
+    bool ca_file_none;
     const char *ca_file;
     const char *ca_path;
     const char *dh_file;
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 669f941b..0ef0f31f 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -693,7 +693,7 @@ init_ssl(const struct options *options, struct tls_root_ctx 
*new_ctx)
         }
     }
 
-    if (options->ca_file || options->ca_path)
+    if ((!options->ca_file_none && options->ca_file) || options->ca_path)
     {
         tls_ctx_load_ca(new_ctx, options->ca_file, options->ca_file_inline,
                         options->ca_path, options->tls_server);
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 08ef6ffa..e9ac5271 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -270,6 +270,7 @@ struct tls_options
     uint8_t *verify_hash;
     hash_algo_type verify_hash_algo;
     char *x509_username_field;
+    bool ca_file_none;
 
     /* allow openvpn config info to be
      * passed over control channel */
diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c
index 25395b27..61873e6f 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -720,7 +720,7 @@ 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)
+    if ((opt->ca_file_none || cert_depth == 1) && opt->verify_hash)
     {
         struct buffer ca_hash = {0};
 
diff --git a/src/openvpn/ssl_verify_mbedtls.c b/src/openvpn/ssl_verify_mbedtls.c
index fd31bbbd..147fb983 100644
--- a/src/openvpn/ssl_verify_mbedtls.c
+++ b/src/openvpn/ssl_verify_mbedtls.c
@@ -63,7 +63,7 @@ verify_callback(void *session_obj, mbedtls_x509_crt *cert, 
int cert_depth,
     cert_hash_remember(session, cert_depth, &cert_fingerprint);
 
     /* did peer present cert which was signed by our root cert? */
-    if (*flags != 0)
+    if (*flags != 0 && !session->opt->ca_file_none)
     {
         int ret = 0;
         char errstr[512] = { 0 };
diff --git a/src/openvpn/ssl_verify_openssl.c b/src/openvpn/ssl_verify_openssl.c
index 9b984751..10d351f4 100644
--- a/src/openvpn/ssl_verify_openssl.c
+++ b/src/openvpn/ssl_verify_openssl.c
@@ -66,7 +66,7 @@ verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
     cert_hash_remember(session, X509_STORE_CTX_get_error_depth(ctx), 
&cert_hash);
 
     /* did peer present cert which was signed by our root cert? */
-    if (!preverify_ok)
+    if (!preverify_ok && !session->opt->ca_file_none)
     {
         /* get the X509 name */
         char *subject = x509_get_subject(current_cert, &gc);
-- 
2.17.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