summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_openssl.c
diff options
context:
space:
mode:
authorAndrey Suvorov <a.suvorov@f5.com>2021-08-17 16:52:32 -0700
committerAndrey Suvorov <a.suvorov@f5.com>2021-08-17 16:52:32 -0700
commite0aa132172f03fe7c31484ce7d301813b5dacb89 (patch)
tree86c83ac1ffc2c842d99aca6ba47c7d7ad67ca82d /src/nxt_openssl.c
parent3bd60e317c142f4596bdc0ef4747ea0f2cc03503 (diff)
downloadunit-e0aa132172f03fe7c31484ce7d301813b5dacb89.tar.gz
unit-e0aa132172f03fe7c31484ce7d301813b5dacb89.tar.bz2
Added TLS session tickets support.
Diffstat (limited to 'src/nxt_openssl.c')
-rw-r--r--src/nxt_openssl.c321
1 files changed, 321 insertions, 0 deletions
diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c
index 297e11cf..273ca7f4 100644
--- a/src/nxt_openssl.c
+++ b/src/nxt_openssl.c
@@ -11,6 +11,8 @@
#include <openssl/err.h>
#include <openssl/rand.h>
#include <openssl/x509v3.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
typedef struct {
@@ -50,6 +52,12 @@ static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
static nxt_int_t nxt_ssl_conf_commands(nxt_task_t *task, SSL_CTX *ctx,
nxt_conf_value_t *value, nxt_mp_t *mp);
#endif
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+static nxt_int_t nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx,
+ nxt_tls_init_t *tls_init, nxt_mp_t *mp);
+static int nxt_tls_ticket_key_callback(SSL *s, unsigned char *name,
+ unsigned char *iv, EVP_CIPHER_CTX *ectx,HMAC_CTX *hctx, int enc);
+#endif
static void nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size,
time_t timeout);
static nxt_uint_t nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert,
@@ -350,6 +358,12 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
nxt_ssl_session_cache(ctx, tls_init->cache_size, tls_init->timeout);
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+ if (nxt_tls_ticket_keys(task, ctx, tls_init, mp) != NXT_OK) {
+ goto fail;
+ }
+#endif
+
SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
if (conf->ca_certificate != NULL) {
@@ -587,6 +601,241 @@ fail:
#endif
+#if (NXT_HAVE_OPENSSL_TLSEXT)
+
+static nxt_int_t
+nxt_tls_ticket_keys(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_init_t *tls_init,
+ nxt_mp_t *mp)
+{
+ uint32_t i;
+ nxt_int_t ret;
+ nxt_str_t value;
+ nxt_uint_t count;
+ nxt_conf_value_t *member, *tickets_conf;
+ nxt_tls_ticket_t *ticket;
+ nxt_tls_tickets_t *tickets;
+ u_char buf[80];
+
+ tickets_conf = tls_init->tickets_conf;
+
+ if (tickets_conf == NULL) {
+ goto no_ticket;
+ }
+
+ if (nxt_conf_type(tickets_conf) == NXT_CONF_BOOLEAN) {
+ if (nxt_conf_get_boolean(tickets_conf) == 0) {
+ goto no_ticket;
+ }
+
+ return NXT_OK;
+ }
+
+ if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) {
+ count = nxt_conf_array_elements_count(tickets_conf);
+
+ if (count == 0) {
+ goto no_ticket;
+ }
+
+ } else {
+ /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */
+ count = 1;
+ }
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+ tickets = nxt_mp_get(mp, sizeof(nxt_tls_tickets_t)
+ + count * sizeof(nxt_tls_ticket_t));
+ if (nxt_slow_path(tickets == NULL)) {
+ return NXT_ERROR;
+ }
+
+ tickets->count = count;
+ tls_init->conf->tickets = tickets;
+ i = 0;
+
+ do {
+ ticket = &tickets->tickets[i];
+
+ i++;
+
+ if (nxt_conf_type(tickets_conf) == NXT_CONF_ARRAY) {
+ member = nxt_conf_get_array_element(tickets_conf, count - i);
+ if (member == NULL) {
+ break;
+ }
+
+ } else {
+ /* nxt_conf_type(tickets_conf) == NXT_CONF_STRING */
+ member = tickets_conf;
+ }
+
+ nxt_conf_get_string(member, &value);
+
+ ret = nxt_openssl_base64_decode(buf, 80, value.start, value.length);
+ if (nxt_slow_path(ret == NXT_ERROR)) {
+ return NXT_ERROR;
+ }
+
+ if (ret == 48) {
+ ticket->aes128 = 1;
+ nxt_memcpy(ticket->aes_key, buf + 16, 16);
+ nxt_memcpy(ticket->hmac_key, buf + 32, 16);
+
+ } else {
+ ticket->aes128 = 0;
+ nxt_memcpy(ticket->hmac_key, buf + 16, 32);
+ nxt_memcpy(ticket->aes_key, buf + 48, 32);
+ }
+
+ nxt_memcpy(ticket->name, buf, 16);
+ } while (i < count);
+
+ if (SSL_CTX_set_tlsext_ticket_key_cb(ctx, nxt_tls_ticket_key_callback)
+ == 0)
+ {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "Unit was built with Session Tickets support, however, "
+ "now it is linked dynamically to an OpenSSL library "
+ "which has no tlsext support, therefore Session Tickets "
+ "are not available");
+
+ return NXT_ERROR;
+ }
+
+ return NXT_OK;
+
+#else
+ nxt_alert(task, "Setting custom session ticket keys is not supported with "
+ "this version of OpenSSL library");
+
+ return NXT_ERROR;
+
+#endif
+
+no_ticket:
+
+ SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET);
+
+ return NXT_OK;
+}
+
+
+#ifdef SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB
+
+static int
+nxt_tls_ticket_key_callback(SSL *s, unsigned char *name, unsigned char *iv,
+ EVP_CIPHER_CTX *ectx, HMAC_CTX *hctx, int enc)
+{
+ size_t size;
+ nxt_uint_t i;
+ nxt_conn_t *c;
+ const EVP_MD *digest;
+ const EVP_CIPHER *cipher;
+ nxt_tls_ticket_t *ticket;
+ nxt_openssl_conn_t *tls;
+
+ c = SSL_get_ex_data(s, nxt_openssl_connection_index);
+
+ if (nxt_slow_path(c == NULL)) {
+ nxt_thread_log_alert("SSL_get_ex_data() failed");
+ return -1;
+ }
+
+ tls = c->u.tls;
+ ticket = tls->conf->tickets->tickets;
+
+#ifdef OPENSSL_NO_SHA256
+ digest = EVP_sha1();
+#else
+ digest = EVP_sha256();
+#endif
+
+ if (enc == 1) {
+ /* encrypt session ticket */
+
+ nxt_debug(c->socket.task, "TLS session ticket encrypt");
+
+ if (ticket[0].aes128 == 1) {
+ cipher = EVP_aes_128_cbc();
+ size = 16;
+
+ } else {
+ cipher = EVP_aes_256_cbc();
+ size = 32;
+ }
+
+ if (RAND_bytes(iv, EVP_CIPHER_iv_length(cipher)) != 1) {
+ nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
+ "RAND_bytes() failed");
+ return -1;
+ }
+
+ if (EVP_EncryptInit_ex(ectx, cipher, NULL, ticket[0].aes_key, iv)
+ != 1)
+ {
+ nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
+ "EVP_EncryptInit_ex() failed");
+ return -1;
+ }
+
+ if (HMAC_Init_ex(hctx, ticket[0].hmac_key, size, digest, NULL) != 1) {
+ nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
+ "HMAC_Init_ex() failed");
+ return -1;
+ }
+
+ nxt_memcpy(name, ticket[0].name, 16);
+
+ return 1;
+
+ } else {
+ /* decrypt session ticket */
+
+ for (i = 0; i < tls->conf->tickets->count; i++) {
+ if (nxt_memcmp(name, ticket[i].name, 16) == 0) {
+ goto found;
+ }
+ }
+
+ nxt_debug(c->socket.task, "TLS session ticket decrypt, key not found");
+
+ return 0;
+
+ found:
+
+ nxt_debug(c->socket.task,
+ "TLS session ticket decrypt, key number: \"%d\"", i);
+
+ if (ticket[i].aes128 == 1) {
+ cipher = EVP_aes_128_cbc();
+ size = 16;
+
+ } else {
+ cipher = EVP_aes_256_cbc();
+ size = 32;
+ }
+
+ if (EVP_DecryptInit_ex(ectx, cipher, NULL, ticket[i].aes_key, iv) != 1) {
+ nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
+ "EVP_DecryptInit_ex() failed");
+ return -1;
+ }
+
+ if (HMAC_Init_ex(hctx, ticket[i].hmac_key, size, digest, NULL) != 1) {
+ nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
+ "HMAC_Init_ex() failed");
+ return -1;
+ }
+
+ return (i == 0) ? 1 : 2 /* renew */;
+ }
+}
+
+#endif /* SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB */
+
+#endif /* NXT_HAVE_OPENSSL_TLSEXT */
+
static void
nxt_ssl_session_cache(SSL_CTX *ctx, size_t cache_size, time_t timeout)
@@ -904,6 +1153,11 @@ nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
bundle = bundle->next;
} while (bundle != NULL);
+ if (conf->tickets) {
+ nxt_memzero(conf->tickets->tickets,
+ conf->tickets->count * sizeof(nxt_tls_ticket_t));
+ }
+
#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
&& OPENSSL_VERSION_NUMBER < 0x1010101fL)
RAND_keep_random_devices_open(0);
@@ -1565,3 +1819,70 @@ nxt_openssl_copy_error(u_char *p, u_char *end)
return p;
}
+
+
+nxt_int_t
+nxt_openssl_base64_decode(u_char *d, size_t dlen, const u_char *s, size_t slen)
+{
+ BIO *bio, *b64;
+ nxt_int_t count, ret;
+ u_char buf[128];
+
+ b64 = BIO_new(BIO_f_base64());
+ if (nxt_slow_path(b64 == NULL)) {
+ goto error;
+ }
+
+ bio = BIO_new_mem_buf(s, slen);
+ if (nxt_slow_path(bio == NULL)) {
+ goto error;
+ }
+
+ bio = BIO_push(b64, bio);
+
+ BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
+
+ count = 0;
+
+ if (d == NULL) {
+
+ for ( ;; ) {
+ ret = BIO_read(bio, buf, 128);
+
+ if (ret < 0) {
+ goto invalid;
+ }
+
+ count += ret;
+
+ if (ret != 128) {
+ break;
+ }
+ }
+
+ } else {
+ count = BIO_read(bio, d, dlen);
+
+ if (count < 0) {
+ goto invalid;
+ }
+ }
+
+ BIO_free_all(bio);
+
+ return count;
+
+error:
+
+ BIO_vfree(b64);
+ ERR_clear_error();
+
+ return NXT_ERROR;
+
+invalid:
+
+ BIO_free_all(bio);
+ ERR_clear_error();
+
+ return NXT_DECLINED;
+}