summaryrefslogtreecommitdiff
path: root/src/openvpn/ssl_openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvpn/ssl_openssl.c')
-rw-r--r--src/openvpn/ssl_openssl.c248
1 files changed, 160 insertions, 88 deletions
diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
index e589dcd..f23d246 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -5,8 +5,8 @@
* packet encryption, packet authentication, and
* packet compression.
*
- * Copyright (C) 2002-2017 OpenVPN Technologies, Inc. <sales@openvpn.net>
- * Copyright (C) 2010-2017 Fox Crypto B.V. <openvpn@fox-it.com>
+ * Copyright (C) 2002-2018 OpenVPN Inc <sales@openvpn.net>
+ * Copyright (C) 2010-2018 Fox Crypto B.V. <openvpn@fox-it.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -69,7 +69,7 @@
int mydata_index; /* GLOBAL */
void
-tls_init_lib()
+tls_init_lib(void)
{
SSL_library_init();
#ifndef ENABLE_SMALL
@@ -82,7 +82,7 @@ tls_init_lib()
}
void
-tls_free_lib()
+tls_free_lib(void)
{
EVP_cleanup();
#ifndef ENABLE_SMALL
@@ -91,7 +91,7 @@ tls_free_lib()
}
void
-tls_clear_error()
+tls_clear_error(void)
{
ERR_clear_error();
}
@@ -206,16 +206,73 @@ info_callback(INFO_CALLBACK_SSL_CONST SSL *s, int where, int ret)
int
tls_version_max(void)
{
-#if defined(SSL_OP_NO_TLSv1_2)
+#if defined(TLS1_3_VERSION)
+ return TLS_VER_1_3;
+#elif defined(TLS1_2_VERSION) || defined(SSL_OP_NO_TLSv1_2)
return TLS_VER_1_2;
-#elif defined(SSL_OP_NO_TLSv1_1)
+#elif defined(TLS1_1_VERSION) || defined(SSL_OP_NO_TLSv1_1)
return TLS_VER_1_1;
#else
return TLS_VER_1_0;
#endif
}
-void
+/** Convert internal version number to openssl version number */
+static int
+openssl_tls_version(int ver)
+{
+ if (ver == TLS_VER_1_0)
+ {
+ return TLS1_VERSION;
+ }
+ else if (ver == TLS_VER_1_1)
+ {
+ return TLS1_1_VERSION;
+ }
+ else if (ver == TLS_VER_1_2)
+ {
+ return TLS1_2_VERSION;
+ }
+#if defined(TLS1_3_VERSION)
+ else if (ver == TLS_VER_1_3)
+ {
+ return TLS1_3_VERSION;
+ }
+#endif
+ return 0;
+}
+
+static bool
+tls_ctx_set_tls_versions(struct tls_root_ctx *ctx, unsigned int ssl_flags)
+{
+ int tls_ver_min = openssl_tls_version(
+ (ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & SSLF_TLS_VERSION_MIN_MASK);
+ int tls_ver_max = openssl_tls_version(
+ (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK);
+
+ if (!tls_ver_min)
+ {
+ /* Enforce at least TLS 1.0 */
+ int cur_min = SSL_CTX_get_min_proto_version(ctx->ctx);
+ tls_ver_min = cur_min < TLS1_VERSION ? TLS1_VERSION : cur_min;
+ }
+
+ if (!SSL_CTX_set_min_proto_version(ctx->ctx, tls_ver_min))
+ {
+ msg(D_TLS_ERRORS, "%s: failed to set minimum TLS version", __func__);
+ return false;
+ }
+
+ if (tls_ver_max && !SSL_CTX_set_max_proto_version(ctx->ctx, tls_ver_max))
+ {
+ msg(D_TLS_ERRORS, "%s: failed to set maximum TLS version", __func__);
+ return false;
+ }
+
+ return true;
+}
+
+bool
tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags)
{
ASSERT(NULL != ctx);
@@ -223,41 +280,21 @@ tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags)
/* default certificate verification flags */
int flags = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
- /* process SSL options including minimum TLS version we will accept from peer */
- {
- long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_NO_TICKET | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
- int tls_ver_max = TLS_VER_UNSPEC;
- const int tls_ver_min =
- (ssl_flags >> SSLF_TLS_VERSION_MIN_SHIFT) & SSLF_TLS_VERSION_MIN_MASK;
-
- tls_ver_max =
- (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK;
- if (tls_ver_max <= TLS_VER_UNSPEC)
- {
- tls_ver_max = tls_version_max();
- }
-
- if (tls_ver_min > TLS_VER_1_0 || tls_ver_max < TLS_VER_1_0)
- {
- sslopt |= SSL_OP_NO_TLSv1;
- }
-#ifdef SSL_OP_NO_TLSv1_1
- if (tls_ver_min > TLS_VER_1_1 || tls_ver_max < TLS_VER_1_1)
- {
- sslopt |= SSL_OP_NO_TLSv1_1;
- }
-#endif
-#ifdef SSL_OP_NO_TLSv1_2
- if (tls_ver_min > TLS_VER_1_2 || tls_ver_max < TLS_VER_1_2)
- {
- sslopt |= SSL_OP_NO_TLSv1_2;
- }
+ /* process SSL options */
+ long sslopt = SSL_OP_SINGLE_DH_USE | SSL_OP_NO_TICKET;
+#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+ sslopt |= SSL_OP_CIPHER_SERVER_PREFERENCE;
#endif
#ifdef SSL_OP_NO_COMPRESSION
- /* Disable compression - flag not available in OpenSSL 0.9.8 */
- sslopt |= SSL_OP_NO_COMPRESSION;
+ /* Disable compression - flag not available in OpenSSL 0.9.8 */
+ sslopt |= SSL_OP_NO_COMPRESSION;
#endif
- SSL_CTX_set_options(ctx->ctx, sslopt);
+
+ SSL_CTX_set_options(ctx->ctx, sslopt);
+
+ if (!tls_ctx_set_tls_versions(ctx, ssl_flags))
+ {
+ return false;
}
#ifdef SSL_MODE_RELEASE_BUFFERS
@@ -280,6 +317,8 @@ tls_ctx_set_options(struct tls_root_ctx *ctx, unsigned int ssl_flags)
SSL_CTX_set_verify(ctx->ctx, flags, verify_callback);
SSL_CTX_set_info_callback(ctx->ctx, info_callback);
+
+ return true;
}
void
@@ -384,6 +423,40 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers)
}
void
+tls_ctx_set_cert_profile(struct tls_root_ctx *ctx, const char *profile)
+{
+#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
+ /* OpenSSL does not have certificate profiles, but a complex set of
+ * callbacks that we could try to implement to achieve something similar.
+ * For now, use OpenSSL's security levels to achieve similar (but not equal)
+ * behaviour. */
+ if (!profile || 0 == strcmp(profile, "legacy"))
+ {
+ SSL_CTX_set_security_level(ctx->ctx, 1);
+ }
+ else if (0 == strcmp(profile, "preferred"))
+ {
+ SSL_CTX_set_security_level(ctx->ctx, 2);
+ }
+ else if (0 == strcmp(profile, "suiteb"))
+ {
+ SSL_CTX_set_security_level(ctx->ctx, 3);
+ SSL_CTX_set_cipher_list(ctx->ctx, "SUITEB128");
+ }
+ else
+ {
+ msg(M_FATAL, "ERROR: Invalid cert profile: %s", profile);
+ }
+#else
+ if (profile)
+ {
+ msg(M_WARN, "WARNING: OpenSSL 1.0.1 does not support --tls-cert-profile"
+ ", ignoring user-set profile: '%s'", profile);
+ }
+#endif
+}
+
+void
tls_ctx_check_cert_time(const struct tls_root_ctx *ctx)
{
int ret;
@@ -487,15 +560,7 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name
/* Generate a new ECDH key for each SSL session (for non-ephemeral ECDH) */
SSL_CTX_set_options(ctx->ctx, SSL_OP_SINGLE_ECDH_USE);
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L
- /* OpenSSL 1.0.2 and newer can automatically handle ECDH parameter loading */
- if (NULL == curve_name)
- {
- SSL_CTX_set_ecdh_auto(ctx->ctx, 1);
- return;
- }
-#endif
- /* For older OpenSSL, we'll have to do the parameter loading on our own */
+
if (curve_name != NULL)
{
/* Use user supplied curve if given */
@@ -504,14 +569,17 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name
}
else
{
- /* Extract curve from key */
+#if OPENSSL_VERSION_NUMBER >= 0x10002000L
+ /* OpenSSL 1.0.2 and newer can automatically handle ECDH parameter
+ * loading */
+ SSL_CTX_set_ecdh_auto(ctx->ctx, 1);
+ return;
+#else
+ /* For older OpenSSL we have to extract the curve from key on our own */
EC_KEY *eckey = NULL;
const EC_GROUP *ecgrp = NULL;
EVP_PKEY *pkey = NULL;
-#if OPENSSL_VERSION_NUMBER >= 0x10002000L && !defined(LIBRESSL_VERSION_NUMBER)
- pkey = SSL_CTX_get0_privatekey(ctx->ctx);
-#else
/* Little hack to get private key ref from SSL_CTX, yay OpenSSL... */
SSL *ssl = SSL_new(ctx->ctx);
if (!ssl)
@@ -520,7 +588,6 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name
}
pkey = SSL_get_privatekey(ssl);
SSL_free(ssl);
-#endif
msg(D_TLS_DEBUG, "Extracting ECDH curve from private key");
@@ -529,6 +596,7 @@ tls_ctx_load_ecdh_params(struct tls_root_ctx *ctx, const char *curve_name
{
nid = EC_GROUP_get_curve_name(ecgrp);
}
+#endif
}
/* Translate NID back to name , just for kicks */
@@ -713,7 +781,7 @@ tls_ctx_add_extra_certs(struct tls_root_ctx *ctx, BIO *bio)
for (;; )
{
cert = NULL;
- if (!PEM_read_bio_X509(bio, &cert, 0, NULL)) /* takes ownership of cert */
+ if (!PEM_read_bio_X509(bio, &cert, NULL, NULL)) /* takes ownership of cert */
{
break;
}
@@ -811,12 +879,6 @@ tls_ctx_load_cert_file(struct tls_root_ctx *ctx, const char *cert_file,
tls_ctx_load_cert_file_and_copy(ctx, cert_file, cert_file_inline, NULL);
}
-void
-tls_ctx_free_cert_file(X509 *x509)
-{
- X509_free(x509);
-}
-
int
tls_ctx_load_priv_file(struct tls_root_ctx *ctx, const char *priv_key_file,
const char *priv_key_file_inline
@@ -1080,6 +1142,13 @@ tls_ctx_use_external_private_key(struct tls_root_ctx *ctx,
ASSERT(pkey); /* NULL before SSL_CTX_use_certificate() is called */
pub_rsa = EVP_PKEY_get0_RSA(pkey);
+ /* Certificate might not be RSA but DSA or EC */
+ if (!pub_rsa)
+ {
+ crypto_msg(M_WARN, "management-external-key requires a RSA certificate");
+ goto err;
+ }
+
/* initialize RSA object */
const BIGNUM *n = NULL;
const BIGNUM *e = NULL;
@@ -1114,7 +1183,7 @@ err:
{
if (rsa_meth)
{
- free(rsa_meth);
+ RSA_meth_free(rsa_meth);
}
}
crypto_msg(M_FATAL, "Cannot enable SSL external private key capability");
@@ -1330,7 +1399,7 @@ static time_t biofp_last_open; /* GLOBAL */
static const int biofp_reopen_interval = 600; /* GLOBAL */
static void
-close_biofp()
+close_biofp(void)
{
if (biofp)
{
@@ -1340,7 +1409,7 @@ close_biofp()
}
static void
-open_biofp()
+open_biofp(void)
{
const time_t current = time(NULL);
const pid_t pid = getpid();
@@ -1386,23 +1455,6 @@ bio_debug_oc(const char *mode, BIO *bio)
#endif /* ifdef BIO_DEBUG */
/*
- * OpenVPN's interface to SSL/TLS authentication,
- * encryption, and decryption is exclusively
- * through "memory BIOs".
- */
-static BIO *
-getbio(BIO_METHOD *type, const char *desc)
-{
- BIO *ret;
- ret = BIO_new(type);
- if (!ret)
- {
- crypto_msg(M_FATAL, "Error creating %s BIO", desc);
- }
- return ret;
-}
-
-/*
* Write to an OpenSSL BIO in non-blocking mode.
*/
static int
@@ -1543,9 +1595,9 @@ key_state_ssl_init(struct key_state_ssl *ks_ssl, const struct tls_root_ctx *ssl_
* from verify callback*/
SSL_set_ex_data(ks_ssl->ssl, mydata_index, session);
- ks_ssl->ssl_bio = getbio(BIO_f_ssl(), "ssl_bio");
- ks_ssl->ct_in = getbio(BIO_s_mem(), "ct_in");
- ks_ssl->ct_out = getbio(BIO_s_mem(), "ct_out");
+ ASSERT((ks_ssl->ssl_bio = BIO_new(BIO_f_ssl())));
+ ASSERT((ks_ssl->ct_in = BIO_new(BIO_s_mem())));
+ ASSERT((ks_ssl->ct_out = BIO_new(BIO_s_mem())));
#ifdef BIO_DEBUG
bio_debug_oc("open ssl_bio", ks_ssl->ssl_bio);
@@ -1686,18 +1738,36 @@ print_details(struct key_state_ssl *ks_ssl, const char *prefix)
EVP_PKEY *pkey = X509_get_pubkey(cert);
if (pkey != NULL)
{
- if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA && EVP_PKEY_get0_RSA(pkey) != NULL)
+ if ((EVP_PKEY_id(pkey) == EVP_PKEY_RSA) && (EVP_PKEY_get0_RSA(pkey) != NULL))
{
RSA *rsa = EVP_PKEY_get0_RSA(pkey);
openvpn_snprintf(s2, sizeof(s2), ", %d bit RSA",
RSA_bits(rsa));
}
- else if (EVP_PKEY_id(pkey) == EVP_PKEY_DSA && EVP_PKEY_get0_DSA(pkey) != NULL)
+ else if ((EVP_PKEY_id(pkey) == EVP_PKEY_DSA) && (EVP_PKEY_get0_DSA(pkey) != NULL))
{
DSA *dsa = EVP_PKEY_get0_DSA(pkey);
openvpn_snprintf(s2, sizeof(s2), ", %d bit DSA",
DSA_bits(dsa));
}
+#ifndef OPENSSL_NO_EC
+ else if ((EVP_PKEY_id(pkey) == EVP_PKEY_EC) && (EVP_PKEY_get0_EC_KEY(pkey) != NULL))
+ {
+ EC_KEY *ec = EVP_PKEY_get0_EC_KEY(pkey);
+ const EC_GROUP *group = EC_KEY_get0_group(ec);
+ const char* curve;
+
+ int nid = EC_GROUP_get_curve_name(group);
+ if (nid == 0 || (curve = OBJ_nid2sn(nid)) == NULL)
+ {
+ curve = "Error getting curve name";
+ }
+
+ openvpn_snprintf(s2, sizeof(s2), ", %d bit EC, curve: %s",
+ EC_GROUP_order_bits(group), curve);
+
+ }
+#endif
EVP_PKEY_free(pkey);
}
X509_free(cert);
@@ -1708,7 +1778,8 @@ print_details(struct key_state_ssl *ks_ssl, const char *prefix)
}
void
-show_available_tls_ciphers(const char *cipher_list)
+show_available_tls_ciphers(const char *cipher_list,
+ const char *tls_cert_profile)
{
struct tls_root_ctx tls_ctx;
SSL *ssl;
@@ -1728,6 +1799,7 @@ show_available_tls_ciphers(const char *cipher_list)
crypto_msg(M_FATAL, "Cannot create SSL object");
}
+ tls_ctx_set_cert_profile(&tls_ctx, tls_cert_profile);
tls_ctx_restrict_ciphers(&tls_ctx, cipher_list);
printf("Available TLS Ciphers,\n");
@@ -1758,7 +1830,7 @@ show_available_tls_ciphers(const char *cipher_list)
* in the OpenSSL library.
*/
void
-show_available_curves()
+show_available_curves(void)
{
#ifndef OPENSSL_NO_EC
EC_builtin_curve *curves = NULL;