diff options
-rw-r--r-- | doc/openvpn.8 | 9 | ||||
-rw-r--r-- | src/openvpn/options.c | 16 | ||||
-rw-r--r-- | src/openvpn/ssl.c | 21 | ||||
-rw-r--r-- | src/openvpn/ssl_backend.h | 23 | ||||
-rw-r--r-- | src/openvpn/ssl_common.h | 4 | ||||
-rw-r--r-- | src/openvpn/ssl_openssl.c | 39 | ||||
-rw-r--r-- | src/openvpn/ssl_polarssl.c | 40 |
7 files changed, 148 insertions, 4 deletions
diff --git a/doc/openvpn.8 b/doc/openvpn.8 index b0228fb..c3d95a2 100644 --- a/doc/openvpn.8 +++ b/doc/openvpn.8 @@ -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. diff --git a/src/openvpn/options.c b/src/openvpn/options.c index 82ed902..9dc1531 100644 --- a/src/openvpn/options.c +++ b/src/openvpn/options.c @@ -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]) { diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c index 8f6813d..20c3f3b 100644 --- a/src/openvpn/ssl.c +++ b/src/openvpn/ssl.c @@ -444,6 +444,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. */ diff --git a/src/openvpn/ssl_backend.h b/src/openvpn/ssl_backend.h index b1dce22..4d2958c 100644 --- a/src/openvpn/ssl_backend.h +++ b/src/openvpn/ssl_backend.h @@ -101,6 +101,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. * * @param ctx TLS context to initialise diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h index 0d818ab..66b6492 100644 --- a/src/openvpn/ssl_common.h +++ b/src/openvpn/ssl_common.h @@ -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 diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c index 2e40ebd..adae555 100644 --- a/src/openvpn/ssl_openssl.c +++ b/src/openvpn/ssl_openssl.c @@ -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 */ diff --git a/src/openvpn/ssl_polarssl.c b/src/openvpn/ssl_polarssl.c index 8a917b3..fb73225 100644 --- a/src/openvpn/ssl_polarssl.c +++ b/src/openvpn/ssl_polarssl.c @@ -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); |