]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
TLS version negotiation
authorJames Yonan <james@openvpn.net>
Tue, 11 Jun 2013 04:59:30 +0000 (22:59 -0600)
committerGert Doering <gert@greenie.muc.de>
Fri, 16 Aug 2013 14:35:39 +0000 (16:35 +0200)
Updated the TLS negotiation logic to adaptively try to connect using
the highest TLS version supported by both client and server.
Previously, OpenVPN (when linked with OpenSSL) would always connect
using TLS 1.0.

Also added tls-version-min directive to force a higher TLS version
than 1.0:

tls-version-min <version> ['or-highest'] -- sets the minimum
TLS version we will accept from the peer.  Examples for version
include "1.0" (default), "1.1", or "1.2".  If 'or-highest' is
specified and version is not recognized, we will only accept
the highest TLS version supported by the local SSL implementation.

Examples:

tls-version-min 1.1 -- fail the connection unless peer can
  connect at TLS 1.1 or higher.

tls-version-min 1.2 or-highest -- require that the peer
  connect at TLS 1.2 or higher, however if the local SSL
  implementation doesn't support TLS 1.2 (as it wouldn't
  if linked with an older version of OpenSSL), reduce the
  minimum required version to the highest version supported
  by the local SSL implementation (such as TLS 1.0).  This
  is intended to allow client configurations to target higher
  TLS versions that are supported on the server, even if some
  older clients don't support these versions yet.

 [
    This is a merged patch from on the following commits
    on git://github.com/jamesyonan/openvpn.git

    03a5599202bdc3ba07983dc4efdae387fb8fb436
    d23005413b0e0f28a3c48a6342f494763d5c9b40
 ]

Signed-off-by: James Yonan <james@openvpn.net>
Acked-by: Gert Doering <gert@greenie.muc.de>
Acked-by: Arne Schwabe <arne@rfc2549.org>
URL: http://thread.gmane.org/gmane.network.openvpn.devel/7743
URL: http://thread.gmane.org/gmane.network.openvpn.devel/7744
Message-Id: 51C77F12.1090802@openvpn.net
Signed-off-by: David Sommerseth <davids@redhat.com>
(cherry picked from commit 4b67f9849ab3efe89268e01afddc7795f38d0f64)
Signed-off-by: Gert Doering <gert@greenie.muc.de>
Conflicts:
src/openvpn/ssl_common.h

doc/openvpn.8
src/openvpn/options.c
src/openvpn/ssl.c
src/openvpn/ssl_backend.h
src/openvpn/ssl_common.h
src/openvpn/ssl_openssl.c
src/openvpn/ssl_polarssl.c

index b0228fb20c56ae359745ac0f716d496104c1354d..c3d95a2c6cd4697232004f5618309a370c801e76 100644 (file)
@@ -4225,6 +4225,15 @@ when you built your peer's certificate (see
 above).
 .\"*********************************************************
 .TP
+.B \-\-tls-version-min version ['or-highest']
+Sets the minimum
+TLS version we will accept from the peer (default is "1.0").
+Examples for version
+include "1.0", "1.1", or "1.2".  If 'or-highest' is specified
+and version is not recognized, we will only accept the highest TLS
+version supported by the local SSL implementation.
+.\"*********************************************************
+.TP
 .B \-\-pkcs12 file
 Specify a PKCS #12 file containing local private key,
 local certificate, and root CA certificate.
index 82ed9026565e3a968f1a4ccff1a3a14a5209a4f9..9dc15314606baff0181d333a6b325ffc94414d15 100644 (file)
@@ -567,6 +567,9 @@ static const char usage_message[] =
   "                  by a Certificate Authority in --ca file.\n"
   "--extra-certs file : one or more PEM certs that complete the cert chain.\n"
   "--key file      : Local private key in .pem format.\n"
+  "--tls-version-min <version> ['or-highest'] : sets the minimum TLS version we\n"
+  "    will accept from the peer.  If version is unrecognized and 'or-highest'\n"
+  "    is specified, require max TLS version supported by SSL implementation.\n"
 #ifndef ENABLE_CRYPTO_POLARSSL
   "--pkcs12 file   : PKCS#12 file containing local private key, local certificate\n"
   "                  and optionally the root CA certificate.\n"
@@ -6433,6 +6436,19 @@ add_option (struct options *options,
          options->priv_key_file_inline = p[2];
        }
     }
+  else if (streq (p[0], "tls-version-min") && p[1])
+    {
+      int ver;
+      VERIFY_PERMISSION (OPT_P_GENERAL);
+      ver = tls_version_min_parse(p[1], p[2]);
+      if (ver == TLS_VER_BAD)
+       {
+         msg (msglevel, "unknown tls-version-min parameter: %s", p[1]);
+          goto err;
+       }
+      options->ssl_flags &= ~(SSLF_TLS_VERSION_MASK << SSLF_TLS_VERSION_SHIFT);
+      options->ssl_flags |= (ver << SSLF_TLS_VERSION_SHIFT);
+    }
 #ifndef ENABLE_CRYPTO_POLARSSL
   else if (streq (p[0], "pkcs12") && p[1])
     {
index 8f6813d16e31dea35953da1adeeadb38791d849e..20c3f3ba8dd2162e2bac6c1b62ea887db2df7fbd 100644 (file)
@@ -443,6 +443,27 @@ ssl_put_auth_challenge (const char *cr_str)
 
 #endif
 
+/*
+ * Parse a TLS version string, returning a TLS_VER_x constant.
+ * If version string is not recognized and extra == "or-highest",
+ * return tls_version_max().
+ */
+int
+tls_version_min_parse(const char *vstr, const char *extra)
+{
+  const int max_version = tls_version_max();
+  if (!strcmp(vstr, "1.0") && TLS_VER_1_0 <= max_version)
+    return TLS_VER_1_0;
+  else if (!strcmp(vstr, "1.1") && TLS_VER_1_1 <= max_version)
+    return TLS_VER_1_1;
+  else if (!strcmp(vstr, "1.2") && TLS_VER_1_2 <= max_version)
+    return TLS_VER_1_2;
+  else if (extra && !strcmp(extra, "or-highest"))
+    return max_version;
+  else
+    return TLS_VER_BAD;
+}
+
 /*
  * Initialize SSL context.
  * All files are in PEM format.
index b1dce2214e68368b9b9488650e5fbdbf92cb3cce..4d2958c7167c6e29bd2b57dc715aad1e5268b214 100644 (file)
@@ -100,6 +100,29 @@ void tls_free_lib();
  */
 void tls_clear_error();
 
+/**
+ * Parse a TLS version specifier
+ *
+ * @param vstr         The TLS version string
+ * @param extra                An optional extra parameter, may be NULL
+ *
+ * @return             One of the TLS_VER_x constants or TLS_VER_BAD
+ *                      if a parse error should be flagged.
+ */
+#define TLS_VER_BAD   -1
+#define TLS_VER_1_0    0 /* default */
+#define TLS_VER_1_1    1
+#define TLS_VER_1_2    2
+int tls_version_min_parse(const char *vstr, const char *extra);
+
+/**
+ * Return the maximum TLS version (as a TLS_VER_x constant)
+ * supported by current SSL implementation
+ *
+ * @return             One of the TLS_VER_x constants (but not TLS_VER_BAD).
+ */
+int tls_version_max(void);
+
 /**
  * Initialise a library-specific TLS context for a server.
  *
index 0d818abe4cb84ac00d172f6e97495edb102fc4ee..66b6492c0723ff938b67ee90fda15c62b352f777 100644 (file)
@@ -285,12 +285,14 @@ struct tls_options
   struct env_set *es;
   const struct plugin_list *plugins;
 
-  /* configuration file boolean options */
+  /* configuration file SSL-related boolean and low-permutation options */
 # define SSLF_CLIENT_CERT_NOT_REQUIRED (1<<0)
 # define SSLF_USERNAME_AS_COMMON_NAME  (1<<1)
 # define SSLF_AUTH_USER_PASS_OPTIONAL  (1<<2)
 # define SSLF_OPT_VERIFY               (1<<4)
 # define SSLF_CRL_VERIFY_DIR           (1<<5)
+# define SSLF_TLS_VERSION_SHIFT        6
+# define SSLF_TLS_VERSION_MASK         0xF /* (uses bit positions 6 to 9) */
   unsigned int ssl_flags;
 
 #ifdef MANAGEMENT_DEF_AUTH
index 2e40ebd1ec99881da315d8a2b54dd5e27db9de28..adae55538611a8e94ccddee056b33a8598f67aa6 100644 (file)
@@ -114,7 +114,7 @@ tls_ctx_server_new(struct tls_root_ctx *ctx)
 {
   ASSERT(NULL != ctx);
 
-  ctx->ctx = SSL_CTX_new (TLSv1_server_method ());
+  ctx->ctx = SSL_CTX_new (SSLv23_server_method ());
 
   if (ctx->ctx == NULL)
     msg (M_SSLERR, "SSL_CTX_new TLSv1_server_method");
@@ -127,7 +127,7 @@ tls_ctx_client_new(struct tls_root_ctx *ctx)
 {
   ASSERT(NULL != ctx);
 
-  ctx->ctx = SSL_CTX_new (TLSv1_client_method ());
+  ctx->ctx = SSL_CTX_new (SSLv23_client_method ());
 
   if (ctx->ctx == NULL)
     msg (M_SSLERR, "SSL_CTX_new TLSv1_client_method");
@@ -174,13 +174,46 @@ info_callback (INFO_CALLBACK_SSL_CONST SSL * s, int where, int ret)
     }
 }
 
+/*
+ * Return maximum TLS version supported by local OpenSSL library.
+ * Assume that presence of SSL_OP_NO_TLSvX macro indicates that
+ * TLSvX is supported.
+ */
+int
+tls_version_max(void)
+{
+#if defined(SSL_OP_NO_TLSv1_2)
+  return TLS_VER_1_2;
+#elif defined(SSL_OP_NO_TLSv1_1)
+  return TLS_VER_1_1;
+#else
+  return TLS_VER_1_0;
+#endif
+}
+
 void
 tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags)
 {
   ASSERT(NULL != ctx);
 
+  /* process SSL options including minimum TLS version we will accept from peer */
+  {
+    long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+    const int tls_version_min = (ssl_flags >> SSLF_TLS_VERSION_SHIFT) & SSLF_TLS_VERSION_MASK;
+    if (tls_version_min > TLS_VER_1_0)
+      sslopt |= SSL_OP_NO_TLSv1;
+#ifdef SSL_OP_NO_TLSv1_1
+    if (tls_version_min > TLS_VER_1_1)
+      sslopt |= SSL_OP_NO_TLSv1_1;
+#endif
+#ifdef SSL_OP_NO_TLSv1_2
+    if (tls_version_min > TLS_VER_1_2)
+      sslopt |= SSL_OP_NO_TLSv1_2;
+#endif
+    SSL_CTX_set_options (ctx->ctx, sslopt);
+  }
+
   SSL_CTX_set_session_cache_mode (ctx->ctx, SSL_SESS_CACHE_OFF);
-  SSL_CTX_set_options (ctx->ctx, SSL_OP_SINGLE_DH_USE);
   SSL_CTX_set_default_passwd_cb (ctx->ctx, pem_password_callback);
 
   /* Require peer certificate verification */
index 8a917b34dbb5248dd339442e101764b0dc9b8c7a..fb7322542278ada518229cf4db8f943ba99e58ef 100644 (file)
@@ -501,6 +501,18 @@ void tls_ctx_personalise_random(struct tls_root_ctx *ctx)
     }
 }
 
+int
+tls_version_max(void)
+{
+#if defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_3)
+  return TLS_VER_1_2;
+#elif defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_2)
+  return TLS_VER_1_1;
+#else
+  return TLS_VER_1_0;
+#endif
+}
+
 void key_state_ssl_init(struct key_state_ssl *ks_ssl,
     const struct tls_root_ctx *ssl_ctx, bool is_server, struct tls_session *session)
 {
@@ -550,6 +562,34 @@ void key_state_ssl_init(struct key_state_ssl *ks_ssl,
       /* TODO: PolarSSL does not currently support sending the CA chain to the client */
       ssl_set_ca_chain (ks_ssl->ctx, ssl_ctx->ca_chain, NULL, NULL );
 
+      /* Initialize minimum TLS version */
+      {
+       const int tls_version_min = (session->opt->ssl_flags >> SSLF_TLS_VERSION_SHIFT) & SSLF_TLS_VERSION_MASK;
+       int polar_major;
+       int polar_minor;
+       switch (tls_version_min)
+         {
+         case TLS_VER_1_0:
+         default:
+           polar_major = SSL_MAJOR_VERSION_3;
+           polar_minor = SSL_MINOR_VERSION_1;
+           break;
+#if defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_2)
+         case TLS_VER_1_1:
+           polar_major = SSL_MAJOR_VERSION_3;
+           polar_minor = SSL_MINOR_VERSION_2;
+           break;
+#endif
+#if defined(SSL_MAJOR_VERSION_3) && defined(SSL_MINOR_VERSION_3)
+         case TLS_VER_1_2:
+           polar_major = SSL_MAJOR_VERSION_3;
+           polar_minor = SSL_MINOR_VERSION_3;
+           break;
+#endif
+         }
+       ssl_set_min_version(ks_ssl->ctx, polar_major, polar_minor);
+      }
+
       /* Initialise BIOs */
       ALLOC_OBJ_CLEAR (ks_ssl->ct_in, endless_buffer);
       ALLOC_OBJ_CLEAR (ks_ssl->ct_out, endless_buffer);