X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=src%2Fplugins%2Ftlsopenssl%2Ftls_openssl.c;h=5d172a0adcf248f5ee05bf7e53f3b120d418377b;hb=1a319aadc68c218f741a7cb23acbe70c4addae92;hp=ac8529926bd30ae03581d355965a2bdf8a6c9b08;hpb=0cef5f5d7171e05389beee0e6b4250b366b2b28e;p=vpp.git diff --git a/src/plugins/tlsopenssl/tls_openssl.c b/src/plugins/tlsopenssl/tls_openssl.c index ac8529926bd..5d172a0adcf 100644 --- a/src/plugins/tlsopenssl/tls_openssl.c +++ b/src/plugins/tlsopenssl/tls_openssl.c @@ -27,6 +27,8 @@ #include #include #include +#include +#include #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; @@ -401,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; @@ -409,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 */ @@ -421,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; } @@ -481,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); @@ -502,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) || @@ -598,6 +645,88 @@ openssl_ctx_read (tls_ctx_t *ctx, session_t *ts) return openssl_ctx_read_dtls (ctx, 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) { @@ -615,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"); @@ -659,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. @@ -927,6 +1063,22 @@ openssl_transport_close (tls_ctx_t * ctx) return 0; } +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) { @@ -938,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) { @@ -1010,22 +1141,51 @@ tls_init_ca_chain (void) return (rv < 0 ? -1 : 0); } +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; @@ -1045,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); @@ -1067,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 * @@ -1145,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 ] [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