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.c301
1 files changed, 235 insertions, 66 deletions
diff --git a/src/openvpn/ssl_openssl.c b/src/openvpn/ssl_openssl.c
index 1dfbb23..51669fc 100644
--- a/src/openvpn/ssl_openssl.c
+++ b/src/openvpn/ssl_openssl.c
@@ -35,7 +35,7 @@
#include "syshead.h"
-#if defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL)
+#if defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL)
#include "errlevel.h"
#include "buffer.h"
@@ -56,6 +56,9 @@
#include <openssl/pkcs12.h>
#include <openssl/x509.h>
#include <openssl/crypto.h>
+#ifndef OPENSSL_NO_EC
+#include <openssl/ec.h>
+#endif
/*
* Allocate space in SSL objects in which to store a struct tls_session
@@ -93,62 +96,23 @@ tls_clear_error()
ERR_clear_error ();
}
-/*
- * OpenSSL callback to get a temporary RSA key, mostly
- * used for export ciphers.
- */
-static RSA *
-tmp_rsa_cb (SSL * s, int is_export, int keylength)
-{
- static RSA *rsa_tmp = NULL;
- if (rsa_tmp == NULL)
- {
- int ret = -1;
- BIGNUM *bn = BN_new();
- rsa_tmp = RSA_new();
-
- msg (D_HANDSHAKE, "Generating temp (%d bit) RSA key", keylength);
-
- if(!bn || !BN_set_word(bn, RSA_F4) ||
- !RSA_generate_key_ex(rsa_tmp, keylength, bn, NULL))
- crypto_msg(M_FATAL, "Failed to generate temp RSA key");
-
- if (bn) BN_free( bn );
- }
- return (rsa_tmp);
-}
-
void
-tls_ctx_server_new(struct tls_root_ctx *ctx, unsigned int ssl_flags)
+tls_ctx_server_new(struct tls_root_ctx *ctx)
{
- const int tls_version_max =
- (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK;
-
ASSERT(NULL != ctx);
- if (tls_version_max == TLS_VER_1_0)
- ctx->ctx = SSL_CTX_new (TLSv1_server_method ());
- else
- ctx->ctx = SSL_CTX_new (SSLv23_server_method ());
+ ctx->ctx = SSL_CTX_new (SSLv23_server_method ());
if (ctx->ctx == NULL)
crypto_msg (M_FATAL, "SSL_CTX_new SSLv23_server_method");
-
- SSL_CTX_set_tmp_rsa_callback (ctx->ctx, tmp_rsa_cb);
}
void
-tls_ctx_client_new(struct tls_root_ctx *ctx, unsigned int ssl_flags)
+tls_ctx_client_new(struct tls_root_ctx *ctx)
{
- const int tls_version_max =
- (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_MASK;
-
ASSERT(NULL != ctx);
- if (tls_version_max == TLS_VER_1_0)
- ctx->ctx = SSL_CTX_new (TLSv1_client_method ());
- else
- ctx->ctx = SSL_CTX_new (SSLv23_client_method ());
+ ctx->ctx = SSL_CTX_new (SSLv23_client_method ());
if (ctx->ctx == NULL)
crypto_msg (M_FATAL, "SSL_CTX_new SSLv23_client_method");
@@ -169,6 +133,38 @@ bool tls_ctx_initialised(struct tls_root_ctx *ctx)
return NULL != ctx->ctx;
}
+void
+key_state_export_keying_material(struct key_state_ssl *ssl,
+ struct tls_session *session)
+{
+ if (session->opt->ekm_size > 0)
+ {
+#if (OPENSSL_VERSION_NUMBER >= 0x10001000)
+ unsigned int size = session->opt->ekm_size;
+ struct gc_arena gc = gc_new();
+ unsigned char* ekm = (unsigned char*) gc_malloc(size, true, &gc);
+
+ if (SSL_export_keying_material(ssl->ssl, ekm, size,
+ session->opt->ekm_label, session->opt->ekm_label_size, NULL, 0, 0))
+ {
+ unsigned int len = (size * 2) + 2;
+
+ const char *key = format_hex_ex (ekm, size, len, 0, NULL, &gc);
+ setenv_str (session->opt->es, "exported_keying_material", key);
+
+ dmsg(D_TLS_DEBUG_MED, "%s: exported keying material: %s",
+ __func__, key);
+ }
+ else
+ {
+ msg (M_WARN, "WARNING: Export keying material failed!");
+ setenv_del (session->opt->es, "exported_keying_material");
+ }
+ gc_free(&gc);
+#endif
+ }
+}
+
/*
* Print debugging information on SSL/TLS session negotiation.
*/
@@ -217,14 +213,18 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags)
{
ASSERT(NULL != ctx);
+ /* 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;
- int tls_ver_max =
- (ssl_flags >> SSLF_TLS_VERSION_MAX_SHIFT) & SSLF_TLS_VERSION_MAX_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();
@@ -245,6 +245,9 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags)
SSL_CTX_set_options (ctx->ctx, sslopt);
}
+#ifdef SSL_MODE_RELEASE_BUFFERS
+ SSL_CTX_set_mode (ctx->ctx, SSL_MODE_RELEASE_BUFFERS);
+#endif
SSL_CTX_set_session_cache_mode (ctx->ctx, SSL_SESS_CACHE_OFF);
SSL_CTX_set_default_passwd_cb (ctx->ctx, pem_password_callback);
@@ -252,14 +255,14 @@ tls_ctx_set_options (struct tls_root_ctx *ctx, unsigned int ssl_flags)
#if P2MP_SERVER
if (ssl_flags & SSLF_CLIENT_CERT_NOT_REQUIRED)
{
- msg (M_WARN, "WARNING: POTENTIALLY DANGEROUS OPTION "
- "--client-cert-not-required may accept clients which do not present "
- "a certificate");
+ flags = 0;
+ }
+ else if (ssl_flags & SSLF_CLIENT_CERT_OPTIONAL)
+ {
+ flags = SSL_VERIFY_PEER;
}
- else
#endif
- SSL_CTX_set_verify (ctx->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- verify_callback);
+ SSL_CTX_set_verify (ctx->ctx, flags, verify_callback);
SSL_CTX_set_info_callback (ctx->ctx, info_callback);
}
@@ -275,12 +278,17 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers)
"DEFAULT"
/* Disable export ciphers and openssl's 'low' and 'medium' ciphers */
":!EXP:!LOW:!MEDIUM"
+ /* Disable static (EC)DH keys (no forward secrecy) */
+ ":!kDH:!kECDH"
+ /* Disable DSA private keys */
+ ":!DSS"
/* Disable unsupported TLS modes */
":!PSK:!SRP:!kRSA"))
crypto_msg (M_FATAL, "Failed to set default TLS cipher list.");
return;
}
+ /* Parse supplied cipher list and pass on to OpenSSL */
size_t begin_of_cipher, end_of_cipher;
const char *current_cipher;
@@ -309,8 +317,8 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers)
// Issue warning on missing translation
// %.*s format specifier expects length of type int, so guarantee
// that length is small enough and cast to int.
- msg (M_WARN, "No valid translation found for TLS cipher '%.*s'",
- (int) MIN(current_cipher_len, 256), current_cipher);
+ msg (D_LOW, "No valid translation found for TLS cipher '%.*s'",
+ constrain_int(current_cipher_len, 0, 256), current_cipher);
}
else
{
@@ -319,7 +327,8 @@ tls_ctx_restrict_ciphers(struct tls_root_ctx *ctx, const char *ciphers)
current_cipher_len = strlen(current_cipher);
if (end_of_cipher - begin_of_cipher == current_cipher_len &&
- 0 == memcmp (&ciphers[begin_of_cipher], cipher_pair->openssl_name, end_of_cipher - begin_of_cipher))
+ 0 != memcmp (&ciphers[begin_of_cipher], cipher_pair->iana_name,
+ end_of_cipher - begin_of_cipher))
{
// Non-IANA name used, show warning
msg (M_WARN, "Deprecated TLS cipher name '%s', please use IANA name '%s'", cipher_pair->openssl_name, cipher_pair->iana_name);
@@ -436,6 +445,78 @@ tls_ctx_load_dh_params (struct tls_root_ctx *ctx, const char *dh_file,
DH_free (dh);
}
+void
+tls_ctx_load_ecdh_params (struct tls_root_ctx *ctx, const char *curve_name
+ )
+{
+#ifndef OPENSSL_NO_EC
+ int nid = NID_undef;
+ EC_KEY *ecdh = NULL;
+ const char *sname = NULL;
+
+ /* 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 */
+ msg (D_TLS_DEBUG, "Using user specified ECDH curve (%s)", curve_name);
+ nid = OBJ_sn2nid(curve_name);
+ }
+ else
+ {
+ /* Extract curve from key */
+ EC_KEY *eckey = NULL;
+ const EC_GROUP *ecgrp = NULL;
+ EVP_PKEY *pkey = NULL;
+
+ /* Little hack to get private key ref from SSL_CTX, yay OpenSSL... */
+ SSL ssl;
+ ssl.cert = ctx->ctx->cert;
+ pkey = SSL_get_privatekey(&ssl);
+
+ msg (D_TLS_DEBUG, "Extracting ECDH curve from private key");
+
+ if (pkey != NULL && (eckey = EVP_PKEY_get1_EC_KEY(pkey)) != NULL &&
+ (ecgrp = EC_KEY_get0_group(eckey)) != NULL)
+ nid = EC_GROUP_get_curve_name(ecgrp);
+ }
+
+ /* Translate NID back to name , just for kicks */
+ sname = OBJ_nid2sn(nid);
+ if (sname == NULL) sname = "(Unknown)";
+
+ /* Create new EC key and set as ECDH key */
+ if (NID_undef == nid || NULL == (ecdh = EC_KEY_new_by_curve_name(nid)))
+ {
+ /* Creating key failed, fall back on sane default */
+ ecdh = EC_KEY_new_by_curve_name(NID_secp384r1);
+ const char *source = (NULL == curve_name) ?
+ "extract curve from certificate" : "use supplied curve";
+ msg (D_TLS_DEBUG_LOW,
+ "Failed to %s (%s), using secp384r1 instead.", source, sname);
+ sname = OBJ_nid2sn(NID_secp384r1);
+ }
+
+ if (!SSL_CTX_set_tmp_ecdh(ctx->ctx, ecdh))
+ crypto_msg (M_FATAL, "SSL_CTX_set_tmp_ecdh: cannot add curve");
+
+ msg (D_TLS_DEBUG_LOW, "ECDH curve %s added", sname);
+
+ EC_KEY_free(ecdh);
+#else
+ msg (M_DEBUG, "Your OpenSSL library was built without elliptic curve support."
+ " Skipping ECDH parameter loading.");
+#endif /* OPENSSL_NO_EC */
+}
+
int
tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file,
const char *pkcs12_file_inline,
@@ -501,7 +582,6 @@ tls_ctx_load_pkcs12(struct tls_root_ctx *ctx, const char *pkcs12_file,
/* Load Private Key */
if (!SSL_CTX_use_PrivateKey (ctx->ctx, pkey))
crypto_msg (M_FATAL, "Cannot use private key");
- warn_if_group_others_accessible (pkcs12_file);
/* Check Private Key */
if (!SSL_CTX_check_private_key (ctx->ctx))
@@ -552,7 +632,7 @@ tls_ctx_load_cryptoapi(struct tls_root_ctx *ctx, const char *cryptoapi_cert)
if (!SSL_CTX_use_CryptoAPI_certificate (ctx->ctx, cryptoapi_cert))
crypto_msg (M_FATAL, "Cannot load certificate \"%s\" from Microsoft Certificate Store", cryptoapi_cert);
}
-#endif /* WIN32 */
+#endif /* ENABLE_CRYPTOAPI */
static void
tls_ctx_add_extra_certs (struct tls_root_ctx *ctx, BIO *bio)
@@ -645,7 +725,6 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file,
const char *priv_key_file_inline
)
{
- int status;
SSL_CTX *ssl_ctx = NULL;
BIO *in = NULL;
EVP_PKEY *pkey = NULL;
@@ -678,7 +757,6 @@ tls_ctx_load_priv_file (struct tls_root_ctx *ctx, const char *priv_key_file,
crypto_msg (M_WARN, "Cannot load private key file %s", priv_key_file);
goto end;
}
- warn_if_group_others_accessible (priv_key_file);
/* Check Private Key */
if (!SSL_CTX_check_private_key (ssl_ctx))
@@ -693,6 +771,64 @@ end:
return ret;
}
+void
+tls_ctx_reload_crl(struct tls_root_ctx *ssl_ctx, const char *crl_file,
+ const char *crl_inline)
+{
+ X509_CRL *crl = NULL;
+ BIO *in = NULL;
+
+ X509_STORE *store = SSL_CTX_get_cert_store(ssl_ctx->ctx);
+ if (!store)
+ crypto_msg (M_FATAL, "Cannot get certificate store");
+
+ /* Always start with a cleared CRL list, for that we
+ * we need to manually find the CRL object from the stack
+ * and remove it */
+ for (int i = 0; i < sk_X509_OBJECT_num(store->objs); i++)
+ {
+ X509_OBJECT* obj = sk_X509_OBJECT_value(store->objs, i);
+ ASSERT(obj);
+ if (obj->type == X509_LU_CRL)
+ {
+ sk_X509_OBJECT_delete(store->objs, i);
+ X509_OBJECT_free_contents(obj);
+ OPENSSL_free(obj);
+ }
+ }
+
+ X509_STORE_set_flags (store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+
+ if (!strcmp (crl_file, INLINE_FILE_TAG) && crl_inline)
+ in = BIO_new_mem_buf ((char *)crl_inline, -1);
+ else
+ in = BIO_new_file (crl_file, "r");
+
+ if (in == NULL)
+ {
+ msg (M_WARN, "CRL: cannot read: %s", crl_file);
+ goto end;
+ }
+
+ crl = PEM_read_bio_X509_CRL(in, NULL, NULL, NULL);
+ if (crl == NULL)
+ {
+ msg (M_WARN, "CRL: cannot read CRL from file %s", crl_file);
+ goto end;
+ }
+
+ if (!X509_STORE_add_crl(store, crl))
+ {
+ msg (M_WARN, "CRL: cannot add %s to store", crl_file);
+ goto end;
+ }
+
+end:
+ X509_CRL_free(crl);
+ BIO_free(in);
+}
+
+
#ifdef MANAGMENT_EXTERNAL_KEY
/* encrypt */
@@ -780,10 +916,11 @@ tls_ctx_use_external_private_key (struct tls_root_ctx *ctx,
X509 *cert = NULL;
ASSERT (NULL != ctx);
- ASSERT (NULL != cert);
tls_ctx_load_cert_file_and_copy (ctx, cert_file, cert_file_inline, &cert);
+ ASSERT (NULL != cert);
+
/* allocate custom RSA method object */
ALLOC_OBJ_CLEAR (rsa_meth, RSA_METHOD);
rsa_meth->name = "OpenVPN external private key RSA Method";
@@ -965,11 +1102,7 @@ tls_ctx_load_ca (struct tls_root_ctx *ctx, const char *ca_file,
msg(M_WARN, "WARNING: experimental option --capath %s", ca_path);
else
crypto_msg (M_FATAL, "Cannot add lookup at --capath %s", ca_path);
-#if OPENSSL_VERSION_NUMBER >= 0x00907000L
X509_STORE_set_flags (store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
-#else
- msg(M_WARN, "WARNING: this version of OpenSSL cannot handle CRL files in capath");
-#endif
}
}
@@ -1385,7 +1518,6 @@ show_available_tls_ciphers (const char *cipher_list)
struct tls_root_ctx tls_ctx;
SSL *ssl;
const char *cipher_name;
- const char *print_name;
const tls_cipher_name_pair *pair;
int priority = 0;
@@ -1419,6 +1551,43 @@ show_available_tls_ciphers (const char *cipher_list)
SSL_CTX_free (tls_ctx.ctx);
}
+/*
+ * Show the Elliptic curves that are available for us to use
+ * in the OpenSSL library.
+ */
+void
+show_available_curves()
+{
+#ifndef OPENSSL_NO_EC
+ EC_builtin_curve *curves = NULL;
+ size_t crv_len = 0;
+ size_t n = 0;
+
+ crv_len = EC_get_builtin_curves(NULL, 0);
+ ALLOC_ARRAY(curves, EC_builtin_curve, crv_len);
+ if (EC_get_builtin_curves(curves, crv_len))
+ {
+ printf ("Available Elliptic curves:\n");
+ for (n = 0; n < crv_len; n++)
+ {
+ const char *sname;
+ sname = OBJ_nid2sn(curves[n].nid);
+ if (sname == NULL) sname = "";
+
+ printf("%s\n", sname);
+ }
+ }
+ else
+ {
+ crypto_msg (M_FATAL, "Cannot get list of builtin curves");
+ }
+ free(curves);
+#else
+ msg (M_WARN, "Your OpenSSL library was built without elliptic curve support. "
+ "No curves available.");
+#endif
+}
+
void
get_highest_preference_tls_cipher (char *buf, int size)
{
@@ -1446,4 +1615,4 @@ get_ssl_library_version(void)
return SSLeay_version(SSLEAY_VERSION);
}
-#endif /* defined(ENABLE_SSL) && defined(ENABLE_CRYPTO_OPENSSL) */
+#endif /* defined(ENABLE_CRYPTO) && defined(ENABLE_CRYPTO_OPENSSL) */