summaryrefslogtreecommitdiffhomepage
path: root/src/nxt_openssl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nxt_openssl.c')
-rw-r--r--src/nxt_openssl.c367
1 files changed, 355 insertions, 12 deletions
diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c
index 2fd5d1bf..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 {
@@ -42,15 +44,22 @@ static void nxt_openssl_lock(int mode, int type, const char *file, int line);
static unsigned long nxt_openssl_thread_id(void);
static void nxt_openssl_locks_free(void);
#endif
-static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
- nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_conf_value_t *conf_cmds,
- nxt_bool_t last);
+static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_tls_init_t *tls_init, nxt_bool_t last);
static nxt_int_t nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx,
nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t single);
#if (NXT_HAVE_OPENSSL_CONF_CMD)
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,
nxt_tls_conf_t *conf, nxt_mp_t *mp);
static nxt_int_t nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq,
@@ -265,11 +274,12 @@ nxt_openssl_locks_free(void)
static nxt_int_t
-nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
- nxt_mp_t *mp, nxt_conf_value_t *conf_cmds, nxt_bool_t last)
+nxt_openssl_server_init(nxt_task_t *task, nxt_mp_t *mp,
+ nxt_tls_init_t *tls_init, nxt_bool_t last)
{
SSL_CTX *ctx;
const char *ciphers, *ca_certificate;
+ nxt_tls_conf_t *conf;
STACK_OF(X509_NAME) *list;
nxt_tls_bundle_conf_t *bundle;
@@ -279,6 +289,8 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
return NXT_ERROR;
}
+ conf = tls_init->conf;
+
bundle = conf->bundle;
nxt_assert(bundle != NULL);
@@ -337,13 +349,21 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
}
#if (NXT_HAVE_OPENSSL_CONF_CMD)
- if (conf_cmds != NULL
- && nxt_ssl_conf_commands(task, ctx, conf_cmds, mp) != NXT_OK)
+ if (tls_init->conf_cmds != NULL
+ && nxt_ssl_conf_commands(task, ctx, tls_init->conf_cmds, mp) != NXT_OK)
{
goto fail;
}
#endif
+ 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) {
@@ -581,6 +601,257 @@ 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)
+{
+ if (cache_size == 0) {
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ return;
+ }
+
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+
+ SSL_CTX_sess_set_cache_size(ctx, cache_size);
+
+ SSL_CTX_set_timeout(ctx, (long) timeout);
+}
+
static nxt_uint_t
nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
@@ -782,15 +1053,15 @@ nxt_openssl_servername(SSL *s, int *ad, void *arg)
}
servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name);
- if (nxt_slow_path(servername == NULL)) {
- nxt_log(c->socket.task, NXT_LOG_ALERT, "SSL_get_servername() returned "
- "NULL in server name callback");
- return SSL_TLSEXT_ERR_ALERT_FATAL;
+
+ if (servername == NULL) {
+ nxt_debug(c->socket.task, "SSL_get_servername(): NULL");
+ goto done;
}
str.length = nxt_strlen(servername);
if (str.length == 0) {
- nxt_debug(c->socket.task, "client sent zero-length server name");
+ nxt_debug(c->socket.task, "SSL_get_servername(): \"\" is empty");
goto done;
}
@@ -882,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);
@@ -1543,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;
+}