aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/tlsopenssl/tls_openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/tlsopenssl/tls_openssl.c')
-rw-r--r--src/plugins/tlsopenssl/tls_openssl.c344
1 files changed, 250 insertions, 94 deletions
diff --git a/src/plugins/tlsopenssl/tls_openssl.c b/src/plugins/tlsopenssl/tls_openssl.c
index 3041047a71e..5d172a0adcf 100644
--- a/src/plugins/tlsopenssl/tls_openssl.c
+++ b/src/plugins/tlsopenssl/tls_openssl.c
@@ -27,6 +27,8 @@
#include <ctype.h>
#include <tlsopenssl/tls_openssl.h>
#include <tlsopenssl/tls_bios.h>
+#include <openssl/x509_vfy.h>
+#include <openssl/x509v3.h>
#define MAX_CRYPTO_LEN 64
@@ -38,7 +40,8 @@ openssl_ctx_alloc_w_thread (u32 thread_index)
openssl_main_t *om = &openssl_main;
openssl_ctx_t **ctx;
- pool_get (om->ctx_pool[thread_index], ctx);
+ pool_get_aligned_safe (om->ctx_pool[thread_index], ctx, 0);
+
if (!(*ctx))
*ctx = clib_mem_alloc (sizeof (openssl_ctx_t));
@@ -62,14 +65,15 @@ openssl_ctx_free (tls_ctx_t * ctx)
openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
/* Cleanup ssl ctx unless migrated */
- if (!ctx->is_migrated)
+ if (!(ctx->flags & TLS_CONN_F_MIGRATED))
{
- if (SSL_is_init_finished (oc->ssl) && !ctx->is_passive_close)
+ if (SSL_is_init_finished (oc->ssl) &&
+ !(ctx->flags & TLS_CONN_F_PASSIVE_CLOSE))
SSL_shutdown (oc->ssl);
SSL_free (oc->ssl);
vec_free (ctx->srv_hostname);
-
+ SSL_CTX_free (oc->client_ssl_ctx);
#ifdef HAVE_OPENSSL_ASYNC
openssl_evt_free (ctx->evt_index, ctx->c_thread_index);
#endif
@@ -97,7 +101,7 @@ openssl_ctx_attach (u32 thread_index, void *ctx_ptr)
session_handle_t sh;
openssl_ctx_t **oc;
- pool_get (om->ctx_pool[thread_index], oc);
+ pool_get_aligned_safe (om->ctx_pool[thread_index], oc, 0);
/* Free the old instance instead of looking for an empty spot */
if (*oc)
clib_mem_free (*oc);
@@ -155,8 +159,12 @@ openssl_lctx_get (u32 lctx_index)
return pool_elt_at_index (openssl_main.lctx_pool, lctx_index);
}
+#define ossl_check_err_is_fatal(_ssl, _rv) \
+ if (PREDICT_FALSE (_rv < 0 && SSL_get_error (_ssl, _rv) == SSL_ERROR_SSL)) \
+ return -1;
+
static int
-openssl_read_from_ssl_into_fifo (svm_fifo_t * f, SSL * ssl)
+openssl_read_from_ssl_into_fifo (svm_fifo_t *f, SSL *ssl, u32 max_len)
{
int read, rv, n_fs, i;
const int n_segs = 2;
@@ -167,6 +175,7 @@ openssl_read_from_ssl_into_fifo (svm_fifo_t * f, SSL * ssl)
if (!max_enq)
return 0;
+ max_enq = clib_min (max_len, max_enq);
n_fs = svm_fifo_provision_chunks (f, fs, n_segs, max_enq);
if (n_fs < 0)
return 0;
@@ -174,17 +183,25 @@ openssl_read_from_ssl_into_fifo (svm_fifo_t * f, SSL * ssl)
/* Return early if we can't read anything */
read = SSL_read (ssl, fs[0].data, fs[0].len);
if (read <= 0)
- return 0;
+ {
+ ossl_check_err_is_fatal (ssl, read);
+ return 0;
+ }
- for (i = 1; i < n_fs; i++)
+ if (read == (int) fs[0].len)
{
- rv = SSL_read (ssl, fs[i].data, fs[i].len);
- read += rv > 0 ? rv : 0;
+ for (i = 1; i < n_fs; i++)
+ {
+ rv = SSL_read (ssl, fs[i].data, fs[i].len);
+ read += rv > 0 ? rv : 0;
- if (rv < (int) fs[i].len)
- break;
+ if (rv < (int) fs[i].len)
+ {
+ ossl_check_err_is_fatal (ssl, rv);
+ break;
+ }
+ }
}
-
svm_fifo_enqueue_nocopy (f, read);
return read;
@@ -194,10 +211,10 @@ static int
openssl_write_from_fifo_into_ssl (svm_fifo_t *f, SSL *ssl, u32 max_len)
{
int wrote = 0, rv, i = 0, len;
- const int n_segs = 2;
+ u32 n_segs = 2;
svm_fifo_seg_t fs[n_segs];
- len = svm_fifo_segments (f, 0, fs, n_segs, max_len);
+ len = svm_fifo_segments (f, 0, fs, &n_segs, max_len);
if (len <= 0)
return 0;
@@ -206,7 +223,10 @@ openssl_write_from_fifo_into_ssl (svm_fifo_t *f, SSL *ssl, u32 max_len)
rv = SSL_write (ssl, fs[i].data, fs[i].len);
wrote += (rv > 0) ? rv : 0;
if (rv < (int) fs[i].len)
- break;
+ {
+ ossl_check_err_is_fatal (ssl, rv);
+ break;
+ }
i++;
}
@@ -243,22 +263,18 @@ openssl_check_async_status (tls_ctx_t * ctx, openssl_resume_handler * handler,
static void
openssl_handle_handshake_failure (tls_ctx_t * ctx)
{
- session_t *app_session;
+ /* Failed to renegotiate handshake */
+ if (ctx->flags & TLS_CONN_F_HS_DONE)
+ {
+ tls_notify_app_io_error (ctx);
+ tls_disconnect_transport (ctx);
+ return;
+ }
if (SSL_is_server (((openssl_ctx_t *) ctx)->ssl))
{
- /*
- * Cleanup pre-allocated app session and close transport
- */
- app_session =
- session_get_if_valid (ctx->c_s_index, ctx->c_thread_index);
- if (app_session)
- {
- session_free (app_session);
- ctx->no_app_session = 1;
- ctx->c_s_index = SESSION_INVALID_INDEX;
- tls_disconnect_transport (ctx);
- }
+ ctx->flags |= TLS_CONN_F_NO_APP_SESSION;
+ tls_disconnect_transport (ctx);
}
else
{
@@ -266,6 +282,7 @@ openssl_handle_handshake_failure (tls_ctx_t * ctx)
* Also handles cleanup of the pre-allocated session
*/
tls_notify_app_connected (ctx, SESSION_E_TLS_HANDSHAKE);
+ tls_disconnect_transport (ctx);
}
}
@@ -277,9 +294,9 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session)
while (SSL_in_init (oc->ssl))
{
- if (ctx->resume)
+ if (ctx->flags & TLS_CONN_F_RESUME)
{
- ctx->resume = 0;
+ ctx->flags &= ~TLS_CONN_F_RESUME;
}
else if (!svm_fifo_max_dequeue_cons (tls_session->rx_fifo))
break;
@@ -313,6 +330,10 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session)
if (SSL_in_init (oc->ssl))
return -1;
+ /* Renegotiated handshake, app must not be notified */
+ if (PREDICT_FALSE (ctx->flags & TLS_CONN_F_HS_DONE))
+ return 0;
+
/*
* Handshake complete
*/
@@ -331,16 +352,20 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session)
*/
if (ctx->srv_hostname)
{
- tls_notify_app_connected (ctx, SESSION_E_TLS_HANDSHAKE);
+ openssl_handle_handshake_failure (ctx);
return -1;
}
}
- tls_notify_app_connected (ctx, SESSION_E_NONE);
+ if (tls_notify_app_connected (ctx, SESSION_E_NONE))
+ {
+ tls_disconnect_transport (ctx);
+ return -1;
+ }
}
else
{
/* Need to check transport status */
- if (ctx->is_passive_close)
+ if (ctx->flags & TLS_CONN_F_PASSIVE_CLOSE)
{
openssl_handle_handshake_failure (ctx);
return -1;
@@ -354,7 +379,7 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session)
return -1;
}
}
-
+ ctx->flags |= TLS_CONN_F_HS_DONE;
TLS_DBG (1, "Handshake for %u complete. TLS cipher is %s",
oc->openssl_ctx_index, SSL_get_cipher (oc->ssl));
return rv;
@@ -363,6 +388,8 @@ openssl_ctx_handshake_rx (tls_ctx_t * ctx, session_t * tls_session)
static void
openssl_confirm_app_close (tls_ctx_t * ctx)
{
+ openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
+ SSL_shutdown (oc->ssl);
tls_disconnect_transport (ctx);
session_transport_closed_notify (&ctx->connection);
}
@@ -399,6 +426,14 @@ openssl_ctx_write_tls (tls_ctx_t *ctx, session_t *app_session,
goto check_tls_fifo;
wrote = openssl_write_from_fifo_into_ssl (f, oc->ssl, deq_max);
+
+ /* Unrecoverable protocol error. Reset connection */
+ if (PREDICT_FALSE (wrote < 0))
+ {
+ tls_notify_app_io_error (ctx);
+ return 0;
+ }
+
if (!wrote)
goto check_tls_fifo;
@@ -407,7 +442,8 @@ openssl_ctx_write_tls (tls_ctx_t *ctx, session_t *app_session,
check_tls_fifo:
- if (PREDICT_FALSE (ctx->app_closed && BIO_ctrl_pending (oc->rbio) <= 0))
+ if (PREDICT_FALSE ((ctx->flags & TLS_CONN_F_APP_CLOSED) &&
+ BIO_ctrl_pending (oc->rbio) <= 0))
openssl_confirm_app_close (ctx);
/* Deschedule and wait for deq notification if fifo is almost full */
@@ -419,8 +455,11 @@ check_tls_fifo:
sp->flags |= TRANSPORT_SND_F_DESCHED;
}
else
- /* Request tx reschedule of the app session */
- app_session->flags |= SESSION_F_CUSTOM_TX;
+ {
+ /* Request tx reschedule of the app session */
+ if (wrote)
+ app_session->flags |= SESSION_F_CUSTOM_TX;
+ }
return wrote;
}
@@ -479,7 +518,7 @@ done:
if (read)
tls_add_vpp_q_tx_evt (us);
- if (PREDICT_FALSE (ctx->app_closed &&
+ if (PREDICT_FALSE ((ctx->flags & TLS_CONN_F_APP_CLOSED) &&
!svm_fifo_max_enqueue_prod (us->rx_fifo)))
openssl_confirm_app_close (ctx);
@@ -500,23 +539,33 @@ static inline int
openssl_ctx_read_tls (tls_ctx_t *ctx, session_t *tls_session)
{
openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
+ const u32 max_len = 128 << 10;
session_t *app_session;
- int read;
svm_fifo_t *f;
+ int read;
if (PREDICT_FALSE (SSL_in_init (oc->ssl)))
{
if (openssl_ctx_handshake_rx (ctx, tls_session) < 0)
return 0;
+
+ /* Application might force a session pool realloc on accept */
+ tls_session = session_get_from_handle (ctx->tls_session_handle);
}
app_session = session_get_from_handle (ctx->app_session_handle);
f = app_session->rx_fifo;
- read = openssl_read_from_ssl_into_fifo (f, oc->ssl);
+ read = openssl_read_from_ssl_into_fifo (f, oc->ssl, max_len);
- /* If handshake just completed, session may still be in accepting state */
- if (read && app_session->session_state >= SESSION_STATE_READY)
+ /* Unrecoverable protocol error. Reset connection */
+ if (PREDICT_FALSE (read < 0))
+ {
+ tls_notify_app_io_error (ctx);
+ return 0;
+ }
+
+ if (read)
tls_notify_app_enqueue (ctx, app_session);
if ((SSL_pending (oc->ssl) > 0) ||
@@ -597,6 +646,88 @@ openssl_ctx_read (tls_ctx_t *ctx, session_t *ts)
}
static int
+openssl_set_ckpair (SSL *ssl, u32 ckpair_index)
+{
+ app_cert_key_pair_t *ckpair;
+ BIO *cert_bio;
+ EVP_PKEY *pkey;
+ X509 *srvcert;
+
+ /* Configure a ckpair index only if non-default/test provided */
+ if (ckpair_index == 0)
+ return 0;
+
+ ckpair = app_cert_key_pair_get_if_valid (ckpair_index);
+ if (!ckpair)
+ return -1;
+
+ if (!ckpair->cert || !ckpair->key)
+ {
+ TLS_DBG (1, "tls cert and/or key not configured");
+ return -1;
+ }
+ /*
+ * Set the key and cert
+ */
+ cert_bio = BIO_new (BIO_s_mem ());
+ BIO_write (cert_bio, ckpair->cert, vec_len (ckpair->cert));
+ srvcert = PEM_read_bio_X509 (cert_bio, NULL, NULL, NULL);
+ if (!srvcert)
+ {
+ clib_warning ("unable to parse certificate");
+ return -1;
+ }
+ SSL_use_certificate (ssl, srvcert);
+ BIO_free (cert_bio);
+
+ cert_bio = BIO_new (BIO_s_mem ());
+ BIO_write (cert_bio, ckpair->key, vec_len (ckpair->key));
+ pkey = PEM_read_bio_PrivateKey (cert_bio, NULL, NULL, NULL);
+ if (!pkey)
+ {
+ clib_warning ("unable to parse pkey");
+ return -1;
+ }
+ SSL_use_PrivateKey (ssl, pkey);
+ BIO_free (cert_bio);
+ TLS_DBG (1, "TLS client using ckpair index: %d", ckpair_index);
+ return 0;
+}
+
+static int
+openssl_client_init_verify (SSL *ssl, const char *srv_hostname,
+ int set_hostname_verification,
+ int set_hostname_strict_check)
+{
+ if (set_hostname_verification)
+ {
+ X509_VERIFY_PARAM *param = SSL_get0_param (ssl);
+ if (!param)
+ {
+ TLS_DBG (1, "Couldn't fetch SSL param");
+ return -1;
+ }
+
+ if (set_hostname_strict_check)
+ X509_VERIFY_PARAM_set_hostflags (param,
+ X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
+
+ if (!X509_VERIFY_PARAM_set1_host (param, srv_hostname, 0))
+ {
+ TLS_DBG (1, "Couldn't set hostname for verification");
+ return -1;
+ }
+ SSL_set_verify (ssl, SSL_VERIFY_PEER, 0);
+ }
+ if (!SSL_set_tlsext_host_name (ssl, srv_hostname))
+ {
+ TLS_DBG (1, "Couldn't set hostname");
+ return -1;
+ }
+ return 0;
+}
+
+static int
openssl_ctx_init_client (tls_ctx_t * ctx)
{
long flags = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_COMPRESSION;
@@ -613,30 +744,31 @@ openssl_ctx_init_client (tls_ctx_t * ctx)
return -1;
}
- oc->ssl_ctx = SSL_CTX_new (method);
- if (oc->ssl_ctx == NULL)
+ oc->client_ssl_ctx = SSL_CTX_new (method);
+ if (oc->client_ssl_ctx == NULL)
{
TLS_DBG (1, "SSL_CTX_new returned null");
return -1;
}
- SSL_CTX_set_ecdh_auto (oc->ssl_ctx, 1);
- SSL_CTX_set_mode (oc->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
+ SSL_CTX_set_ecdh_auto (oc->client_ssl_ctx, 1);
+ SSL_CTX_set_mode (oc->client_ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
#ifdef HAVE_OPENSSL_ASYNC
if (om->async)
- SSL_CTX_set_mode (oc->ssl_ctx, SSL_MODE_ASYNC);
+ SSL_CTX_set_mode (oc->client_ssl_ctx, SSL_MODE_ASYNC);
#endif
- rv = SSL_CTX_set_cipher_list (oc->ssl_ctx, (const char *) om->ciphers);
+ rv =
+ SSL_CTX_set_cipher_list (oc->client_ssl_ctx, (const char *) om->ciphers);
if (rv != 1)
{
TLS_DBG (1, "Couldn't set cipher");
return -1;
}
- SSL_CTX_set_options (oc->ssl_ctx, flags);
- SSL_CTX_set_cert_store (oc->ssl_ctx, om->cert_store);
+ SSL_CTX_set_options (oc->client_ssl_ctx, flags);
+ SSL_CTX_set1_cert_store (oc->client_ssl_ctx, om->cert_store);
- oc->ssl = SSL_new (oc->ssl_ctx);
+ oc->ssl = SSL_new (oc->client_ssl_ctx);
if (oc->ssl == NULL)
{
TLS_DBG (1, "Couldn't initialize ssl struct");
@@ -657,12 +789,18 @@ openssl_ctx_init_client (tls_ctx_t * ctx)
SSL_set_bio (oc->ssl, oc->wbio, oc->rbio);
SSL_set_connect_state (oc->ssl);
- rv = SSL_set_tlsext_host_name (oc->ssl, ctx->srv_hostname);
- if (rv != 1)
+ /* Hostname validation and strict check by name are disabled by default */
+ rv = openssl_client_init_verify (oc->ssl, (const char *) ctx->srv_hostname,
+ 0, 0);
+ if (rv)
{
- TLS_DBG (1, "Couldn't set hostname");
+ TLS_DBG (1, "ERROR:verify init failed:%d", rv);
return -1;
}
+ if (openssl_set_ckpair (oc->ssl, ctx->ckpair_index))
+ {
+ TLS_DBG (1, "Couldn't set client certificate-key pair");
+ }
/*
* 2. Do the first steps in the handshake.
@@ -926,6 +1064,22 @@ openssl_transport_close (tls_ctx_t * ctx)
}
static int
+openssl_transport_reset (tls_ctx_t *ctx)
+{
+ if (!openssl_handshake_is_over (ctx))
+ {
+ openssl_handle_handshake_failure (ctx);
+ return 0;
+ }
+
+ session_transport_reset_notify (&ctx->connection);
+ session_transport_closed_notify (&ctx->connection);
+ tls_disconnect_transport (ctx);
+
+ return 0;
+}
+
+static int
openssl_app_close (tls_ctx_t * ctx)
{
openssl_ctx_t *oc = (openssl_ctx_t *) ctx;
@@ -936,30 +1090,9 @@ openssl_app_close (tls_ctx_t * ctx)
if (BIO_ctrl_pending (oc->rbio) <= 0
&& !svm_fifo_max_dequeue_cons (app_session->tx_fifo))
openssl_confirm_app_close (ctx);
- else
- ctx->app_closed = 1;
return 0;
}
-const static tls_engine_vft_t openssl_engine = {
- .ctx_alloc = openssl_ctx_alloc,
- .ctx_alloc_w_thread = openssl_ctx_alloc_w_thread,
- .ctx_free = openssl_ctx_free,
- .ctx_attach = openssl_ctx_attach,
- .ctx_detach = openssl_ctx_detach,
- .ctx_get = openssl_ctx_get,
- .ctx_get_w_thread = openssl_ctx_get_w_thread,
- .ctx_init_server = openssl_ctx_init_server,
- .ctx_init_client = openssl_ctx_init_client,
- .ctx_write = openssl_ctx_write,
- .ctx_read = openssl_ctx_read,
- .ctx_handshake_is_over = openssl_handshake_is_over,
- .ctx_start_listen = openssl_start_listen,
- .ctx_stop_listen = openssl_stop_listen,
- .ctx_transport_close = openssl_transport_close,
- .ctx_app_close = openssl_app_close,
-};
-
int
tls_init_ca_chain (void)
{
@@ -1009,21 +1142,50 @@ tls_init_ca_chain (void)
}
int
+openssl_reinit_ca_chain (void)
+{
+ openssl_main_t *om = &openssl_main;
+
+ /* Remove/free existing x509_store */
+ if (om->cert_store)
+ {
+ X509_STORE_free (om->cert_store);
+ }
+ return tls_init_ca_chain ();
+}
+
+const static tls_engine_vft_t openssl_engine = {
+ .ctx_alloc = openssl_ctx_alloc,
+ .ctx_alloc_w_thread = openssl_ctx_alloc_w_thread,
+ .ctx_free = openssl_ctx_free,
+ .ctx_attach = openssl_ctx_attach,
+ .ctx_detach = openssl_ctx_detach,
+ .ctx_get = openssl_ctx_get,
+ .ctx_get_w_thread = openssl_ctx_get_w_thread,
+ .ctx_init_server = openssl_ctx_init_server,
+ .ctx_init_client = openssl_ctx_init_client,
+ .ctx_write = openssl_ctx_write,
+ .ctx_read = openssl_ctx_read,
+ .ctx_handshake_is_over = openssl_handshake_is_over,
+ .ctx_start_listen = openssl_start_listen,
+ .ctx_stop_listen = openssl_stop_listen,
+ .ctx_transport_close = openssl_transport_close,
+ .ctx_transport_reset = openssl_transport_reset,
+ .ctx_app_close = openssl_app_close,
+ .ctx_reinit_cachain = openssl_reinit_ca_chain,
+};
+
+int
tls_openssl_set_ciphers (char *ciphers)
{
openssl_main_t *om = &openssl_main;
- int i;
if (!ciphers)
{
return -1;
}
- vec_validate (om->ciphers, strlen (ciphers) - 1);
- for (i = 0; i < vec_len (om->ciphers); i++)
- {
- om->ciphers[i] = toupper (ciphers[i]);
- }
+ vec_validate_init_c_string (om->ciphers, ciphers, strlen (ciphers));
return 0;
@@ -1043,12 +1205,6 @@ tls_openssl_init (vlib_main_t * vm)
SSL_library_init ();
SSL_load_error_strings ();
- if (tls_init_ca_chain ())
- {
- clib_warning ("failed to initialize TLS CA chain");
- return 0;
- }
-
vec_validate (om->ctx_pool, num_threads - 1);
vec_validate (om->rx_bufs, num_threads - 1);
vec_validate (om->tx_bufs, num_threads - 1);
@@ -1065,14 +1221,18 @@ tls_openssl_init (vlib_main_t * vm)
tls_openssl_set_ciphers
("ALL:!ADH:!LOW:!EXP:!MD5:!RC4-SHA:!DES-CBC3-SHA:@STRENGTH");
+ if (tls_init_ca_chain ())
+ {
+ clib_warning ("failed to initialize TLS CA chain");
+ return 0;
+ }
+
return error;
}
-/* *INDENT-OFF* */
VLIB_INIT_FUNCTION (tls_openssl_init) =
{
.runs_after = VLIB_INITS("tls_init"),
};
-/* *INDENT-ON* */
#ifdef HAVE_OPENSSL_ASYNC
static clib_error_t *
@@ -1143,22 +1303,18 @@ tls_openssl_set_command_fn (vlib_main_t * vm, unformat_input_t * input,
return 0;
}
-/* *INDENT-OFF* */
VLIB_CLI_COMMAND (tls_openssl_set_command, static) =
{
.path = "tls openssl set",
.short_help = "tls openssl set [engine <engine name>] [alg [algorithm] [async]",
.function = tls_openssl_set_command_fn,
};
-/* *INDENT-ON* */
#endif
-/* *INDENT-OFF* */
VLIB_PLUGIN_REGISTER () = {
.version = VPP_BUILD_VER,
.description = "Transport Layer Security (TLS) Engine, OpenSSL Based",
};
-/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON