summaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorAndrey Suvorov <a.suvorov@f5.com>2021-03-24 13:19:36 -0700
committerAndrey Suvorov <a.suvorov@f5.com>2021-03-24 13:19:36 -0700
commitd2b0882d89f29fea84b457e0709b6980c8a30a57 (patch)
tree5bf41ab489a5866cfcb28ea35d36623110079d53 /src
parentd62192738f2db9e22e1357daa7b7dccdce3c783f (diff)
downloadunit-d2b0882d89f29fea84b457e0709b6980c8a30a57.tar.gz
unit-d2b0882d89f29fea84b457e0709b6980c8a30a57.tar.bz2
Added ability to configure multiple certificates on a listener.
The certificate is selected by matching the arriving SNI to the common name and the alternatives names. If no certificate matches the name, the first bundle in the array is chosen.
Diffstat (limited to 'src')
-rw-r--r--src/nxt_conf_validation.c29
-rw-r--r--src/nxt_openssl.c404
-rw-r--r--src/nxt_router.c123
-rw-r--r--src/nxt_tls.h29
4 files changed, 517 insertions, 68 deletions
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index 0e6fc135..8c5d1ec7 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -87,6 +87,8 @@ static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt,
#if (NXT_TLS)
static nxt_int_t nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
+static nxt_int_t nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value);
#endif
static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
@@ -354,7 +356,7 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
{
.name = nxt_string("certificate"),
- .type = NXT_CONF_VLDT_STRING,
+ .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
.validator = nxt_conf_vldt_certificate,
},
@@ -1827,9 +1829,34 @@ static nxt_int_t
nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
{
+ if (nxt_conf_type(value) == NXT_CONF_ARRAY) {
+ if (nxt_conf_array_elements_count(value) == 0) {
+ return nxt_conf_vldt_error(vldt, "The \"certificate\" array "
+ "must contain at least one element.");
+ }
+
+ return nxt_conf_vldt_array_iterator(vldt, value,
+ &nxt_conf_vldt_certificate_element);
+ }
+
+ /* NXT_CONF_STRING */
+
+ return nxt_conf_vldt_certificate_element(vldt, value);
+}
+
+
+static nxt_int_t
+nxt_conf_vldt_certificate_element(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value)
+{
nxt_str_t name;
nxt_conf_value_t *cert;
+ if (nxt_conf_type(value) != NXT_CONF_STRING) {
+ return nxt_conf_vldt_error(vldt, "The \"certificate\" array must "
+ "contain only string values.");
+ }
+
nxt_conf_get_string(value, &name);
cert = nxt_cert_info_get(&name);
diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c
index 835ca8b2..9b86150f 100644
--- a/src/nxt_openssl.c
+++ b/src/nxt_openssl.c
@@ -9,17 +9,19 @@
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/rand.h>
+#include <openssl/x509v3.h>
typedef struct {
- SSL *session;
- nxt_conn_t *conn;
+ SSL *session;
+ nxt_conn_t *conn;
- int ssl_error;
- uint8_t times; /* 2 bits */
- uint8_t handshake; /* 1 bit */
+ int ssl_error;
+ uint8_t times; /* 2 bits */
+ uint8_t handshake; /* 1 bit */
- nxt_buf_mem_t buffer;
+ nxt_tls_conf_t *conf;
+ nxt_buf_mem_t buffer;
} nxt_openssl_conn_t;
@@ -40,8 +42,18 @@ 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);
-static nxt_int_t nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd);
+ nxt_tls_conf_t *conf, nxt_mp_t *mp, 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);
+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,
+ void *data);
+static nxt_int_t nxt_openssl_bundle_hash_insert(nxt_task_t *task,
+ nxt_lvlhsh_t *lvlhsh, nxt_tls_bundle_hash_item_t *item, nxt_mp_t * mp);
+static nxt_int_t nxt_openssl_servername(SSL *s, int *ad, void *arg);
+static nxt_tls_bundle_conf_t *nxt_openssl_find_ctx(nxt_tls_conf_t *conf,
+ nxt_str_t *sn);
static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf);
static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
nxt_conn_t *c);
@@ -245,12 +257,13 @@ nxt_openssl_locks_free(void)
static nxt_int_t
-nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
+nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf,
+ nxt_mp_t *mp, nxt_bool_t last)
{
- SSL_CTX *ctx;
- nxt_fd_t fd;
- const char *ciphers, *ca_certificate;
- STACK_OF(X509_NAME) *list;
+ SSL_CTX *ctx;
+ const char *ciphers, *ca_certificate;
+ STACK_OF(X509_NAME) *list;
+ nxt_tls_bundle_conf_t *bundle;
ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL) {
@@ -258,8 +271,10 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
return NXT_ERROR;
}
- conf->ctx = ctx;
- conf->conn_init = nxt_openssl_conn_init;
+ bundle = conf->bundle;
+ nxt_assert(bundle != NULL);
+
+ bundle->ctx = ctx;
#ifdef SSL_OP_NO_RENEGOTIATION
/* Renegration is not currently supported. */
@@ -287,11 +302,10 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
#endif
- fd = conf->chain_file;
-
- if (nxt_openssl_chain_file(ctx, fd) != NXT_OK) {
- nxt_openssl_log_error(task, NXT_LOG_ALERT,
- "nxt_openssl_chain_file() failed");
+ if (nxt_openssl_chain_file(task, ctx, conf, mp,
+ last && bundle->next == NULL)
+ != NXT_OK)
+ {
goto fail;
}
/*
@@ -350,6 +364,14 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
SSL_CTX_set_client_CA_list(ctx, list);
}
+ if (last) {
+ conf->conn_init = nxt_openssl_conn_init;
+
+ if (bundle->next != NULL) {
+ SSL_CTX_set_tlsext_servername_callback(ctx, nxt_openssl_servername);
+ }
+ }
+
return NXT_OK;
fail:
@@ -366,22 +388,27 @@ fail:
static nxt_int_t
-nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd)
+nxt_openssl_chain_file(nxt_task_t *task, SSL_CTX *ctx, nxt_tls_conf_t *conf,
+ nxt_mp_t *mp, nxt_bool_t single)
{
- BIO *bio;
- X509 *cert, *ca;
- long reason;
- EVP_PKEY *key;
- nxt_int_t ret;
+ BIO *bio;
+ X509 *cert, *ca;
+ long reason;
+ EVP_PKEY *key;
+ nxt_int_t ret;
+ nxt_tls_bundle_conf_t *bundle;
+
+ ret = NXT_ERROR;
+ cert = NULL;
bio = BIO_new(BIO_s_fd());
if (bio == NULL) {
- return NXT_ERROR;
+ goto end;
}
- BIO_set_fd(bio, fd, BIO_CLOSE);
+ bundle = conf->bundle;
- ret = NXT_ERROR;
+ BIO_set_fd(bio, bundle->chain_file, BIO_CLOSE);
cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
if (cert == NULL) {
@@ -392,6 +419,10 @@ nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd)
goto end;
}
+ if (!single && nxt_openssl_cert_get_names(task, cert, conf, mp) != NXT_OK) {
+ goto clean;
+ }
+
for ( ;; ) {
ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
@@ -437,17 +468,319 @@ nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd)
end:
- X509_free(cert);
+ if (ret != NXT_OK) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "nxt_openssl_chain_file() failed");
+ }
+
+clean:
+
BIO_free(bio);
+ X509_free(cert);
return ret;
}
+static nxt_uint_t
+nxt_openssl_cert_get_names(nxt_task_t *task, X509 *cert, nxt_tls_conf_t *conf,
+ nxt_mp_t *mp)
+{
+ int len;
+ nxt_str_t domain, str;
+ X509_NAME *x509_name;
+ nxt_uint_t i, n;
+ GENERAL_NAME *name;
+ nxt_tls_bundle_conf_t *bundle;
+ STACK_OF(GENERAL_NAME) *alt_names;
+ nxt_tls_bundle_hash_item_t *item;
+
+ bundle = conf->bundle;
+
+ alt_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+ if (alt_names != NULL) {
+ n = sk_GENERAL_NAME_num(alt_names);
+
+ for (i = 0; i != n; i++) {
+ name = sk_GENERAL_NAME_value(alt_names, i);
+
+ if (name->type != GEN_DNS) {
+ continue;
+ }
+
+ str.length = ASN1_STRING_length(name->d.dNSName);
+#if OPENSSL_VERSION_NUMBER > 0x10100000L
+ str.start = (u_char *) ASN1_STRING_get0_data(name->d.dNSName);
+#else
+ str.start = ASN1_STRING_data(name->d.dNSName);
+#endif
+
+ domain.start = nxt_mp_nget(mp, str.length);
+ if (nxt_slow_path(domain.start == NULL)) {
+ goto fail;
+ }
+
+ domain.length = str.length;
+ nxt_memcpy_lowcase(domain.start, str.start, str.length);
+
+ item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
+ if (nxt_slow_path(item == NULL)) {
+ goto fail;
+ }
+
+ item->name = domain;
+ item->bundle = bundle;
+
+ if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash,
+ item, mp)
+ == NXT_ERROR)
+ {
+ goto fail;
+ }
+ }
+
+ sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
+
+ } else {
+ x509_name = X509_get_subject_name(cert);
+ len = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
+ NULL, 0);
+ if (len <= 0) {
+ nxt_log(task, NXT_LOG_WARN, "certificate \"%V\" has neither "
+ "Subject Alternative Name nor Common Name", bundle->name);
+ return NXT_OK;
+ }
+
+ domain.start = nxt_mp_nget(mp, len + 1);
+ if (nxt_slow_path(domain.start == NULL)) {
+ return NXT_ERROR;
+ }
+
+ domain.length = X509_NAME_get_text_by_NID(x509_name, NID_commonName,
+ (char *) domain.start,
+ len + 1);
+ nxt_memcpy_lowcase(domain.start, domain.start, domain.length);
+
+ item = nxt_mp_get(mp, sizeof(nxt_tls_bundle_hash_item_t));
+ if (nxt_slow_path(item == NULL)) {
+ return NXT_ERROR;
+ }
+
+ item->name = domain;
+ item->bundle = bundle;
+
+ if (nxt_openssl_bundle_hash_insert(task, &conf->bundle_hash, item,
+ mp)
+ == NXT_ERROR)
+ {
+ return NXT_ERROR;
+ }
+ }
+
+ return NXT_OK;
+
+fail:
+
+ sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
+
+ return NXT_ERROR;
+}
+
+
+static const nxt_lvlhsh_proto_t nxt_openssl_bundle_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ nxt_openssl_bundle_hash_test,
+ nxt_mp_lvlhsh_alloc,
+ nxt_mp_lvlhsh_free,
+};
+
+
+static nxt_int_t
+nxt_openssl_bundle_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ nxt_tls_bundle_hash_item_t *item;
+
+ item = data;
+
+ return nxt_strstr_eq(&lhq->key, &item->name) ? NXT_OK : NXT_DECLINED;
+}
+
+
+static nxt_int_t
+nxt_openssl_bundle_hash_insert(nxt_task_t *task, nxt_lvlhsh_t *lvlhsh,
+ nxt_tls_bundle_hash_item_t *item, nxt_mp_t *mp)
+{
+ nxt_str_t str;
+ nxt_int_t ret;
+ nxt_lvlhsh_query_t lhq;
+ nxt_tls_bundle_hash_item_t *old;
+
+ str = item->name;
+
+ if (item->name.start[0] == '*') {
+ item->name.start++;
+ item->name.length--;
+
+ if (item->name.length == 0 || item->name.start[0] != '.') {
+ nxt_log(task, NXT_LOG_WARN, "ignored invalid name \"%V\" "
+ "in certificate \"%V\": missing \".\" "
+ "after wildcard symbol", &str, item->bundle->name);
+ return NXT_OK;
+ }
+ }
+
+ lhq.pool = mp;
+ lhq.key = item->name;
+ lhq.value = item;
+ lhq.proto = &nxt_openssl_bundle_hash_proto;
+ lhq.replace = 0;
+ lhq.key_hash = nxt_murmur_hash2(item->name.start, item->name.length);
+
+ ret = nxt_lvlhsh_insert(lvlhsh, &lhq);
+ if (nxt_fast_path(ret == NXT_OK)) {
+ nxt_debug(task, "name \"%V\" for certificate \"%V\" is inserted",
+ &str, item->bundle->name);
+ return NXT_OK;
+ }
+
+ if (nxt_fast_path(ret == NXT_DECLINED)) {
+ old = lhq.value;
+ if (old->bundle != item->bundle) {
+ nxt_log(task, NXT_LOG_WARN, "ignored duplicate name \"%V\" "
+ "in certificate \"%V\", identical name appears in \"%V\"",
+ &str, old->bundle->name, item->bundle->name);
+
+ old->bundle = item->bundle;
+ }
+
+ return NXT_OK;
+ }
+
+ return NXT_ERROR;
+}
+
+
+static nxt_int_t
+nxt_openssl_servername(SSL *s, int *ad, void *arg)
+{
+ nxt_str_t str;
+ nxt_uint_t i;
+ nxt_conn_t *c;
+ const char *servername;
+ nxt_tls_conf_t *conf;
+ nxt_openssl_conn_t *tls;
+ nxt_tls_bundle_conf_t *bundle;
+
+ 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 SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ 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;
+ }
+
+ str.length = nxt_strlen(servername);
+ if (str.length == 0) {
+ nxt_debug(c->socket.task, "client sent zero-length server name");
+ goto done;
+ }
+
+ if (servername[0] == '.') {
+ nxt_debug(c->socket.task, "ignored the server name \"%s\": "
+ "leading \".\"", servername);
+ goto done;
+ }
+
+ nxt_debug(c->socket.task, "tls with servername \"%s\"", servername);
+
+ str.start = nxt_mp_nget(c->mem_pool, str.length);
+ if (nxt_slow_path(str.start == NULL)) {
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+
+ nxt_memcpy_lowcase(str.start, (const u_char *) servername, str.length);
+
+ tls = c->u.tls;
+ conf = tls->conf;
+
+ bundle = nxt_openssl_find_ctx(conf, &str);
+
+ if (bundle == NULL) {
+ for (i = 1; i < str.length; i++) {
+ if (str.start[i] == '.') {
+ str.start += i;
+ str.length -= i;
+
+ bundle = nxt_openssl_find_ctx(conf, &str);
+ break;
+ }
+ }
+ }
+
+ if (bundle != NULL) {
+ nxt_debug(c->socket.task, "new tls context found for \"%V\": \"%V\" "
+ "(old: \"%V\")", &str, bundle->name,
+ conf->bundle->name);
+
+ if (bundle != conf->bundle) {
+ if (SSL_set_SSL_CTX(s, bundle->ctx) == NULL) {
+ nxt_openssl_log_error(c->socket.task, NXT_LOG_ALERT,
+ "SSL_set_SSL_CTX() failed");
+
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+ }
+ }
+ }
+
+done:
+
+ return SSL_TLSEXT_ERR_OK;
+}
+
+
+static nxt_tls_bundle_conf_t *
+nxt_openssl_find_ctx(nxt_tls_conf_t *conf, nxt_str_t *sn)
+{
+ nxt_int_t ret;
+ nxt_lvlhsh_query_t lhq;
+ nxt_tls_bundle_hash_item_t *item;
+
+ lhq.key_hash = nxt_murmur_hash2(sn->start, sn->length);
+ lhq.key = *sn;
+ lhq.proto = &nxt_openssl_bundle_hash_proto;
+
+ ret = nxt_lvlhsh_find(&conf->bundle_hash, &lhq);
+ if (ret != NXT_OK) {
+ return NULL;
+ }
+
+ item = lhq.value;
+
+ return item->bundle;
+}
+
+
static void
nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
{
- SSL_CTX_free(conf->ctx);
+ nxt_tls_bundle_conf_t *bundle;
+
+ bundle = conf->bundle;
+ nxt_assert(bundle != NULL);
+
+ do {
+ SSL_CTX_free(bundle->ctx);
+ bundle = bundle->next;
+ } while (bundle != NULL);
#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
&& OPENSSL_VERSION_NUMBER < 0x1010101fL)
@@ -474,7 +807,7 @@ nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
c->u.tls = tls;
nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size);
- ctx = conf->ctx;
+ ctx = conf->bundle->ctx;
s = SSL_new(ctx);
if (s == NULL) {
@@ -483,6 +816,8 @@ nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
}
tls->session = s;
+ /* To pass TLS config to the nxt_openssl_servername() callback. */
+ tls->conf = conf;
tls->conn = c;
ret = SSL_set_fd(s, c->socket.fd);
@@ -504,6 +839,11 @@ nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf, nxt_conn_t *c)
c->sendfile = NXT_CONN_SENDFILE_OFF;
nxt_openssl_conn_handshake(task, c, c->socket.data);
+ /*
+ * TLS configuration might be destroyed after the TLS connection
+ * is established.
+ */
+ tls->conf = NULL;
return;
fail:
diff --git a/src/nxt_router.c b/src/nxt_router.c
index 4be4197a..0ecaefa1 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -51,8 +51,10 @@ typedef struct {
typedef struct {
+ nxt_str_t *name;
nxt_socket_conf_t *socket_conf;
nxt_router_temp_conf_t *temp_conf;
+ nxt_bool_t last;
} nxt_socket_rpc_t;
@@ -116,9 +118,11 @@ static void nxt_router_listen_socket_error(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
#if (NXT_TLS)
static void nxt_router_tls_rpc_create(nxt_task_t *task,
- nxt_router_temp_conf_t *tmcf, nxt_router_tlssock_t *tls);
+ nxt_router_temp_conf_t *tmcf, nxt_router_tlssock_t *tls, nxt_bool_t last);
static void nxt_router_tls_rpc_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
+static nxt_int_t nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf,
+ nxt_conf_value_t *value, nxt_socket_conf_t *skcf);
#endif
static void nxt_router_app_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_app_t *app);
@@ -944,14 +948,15 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data)
}
#if (NXT_TLS)
- qlk = nxt_queue_first(&tmcf->tls);
+ qlk = nxt_queue_last(&tmcf->tls);
- if (qlk != nxt_queue_tail(&tmcf->tls)) {
+ if (qlk != nxt_queue_head(&tmcf->tls)) {
nxt_queue_remove(qlk);
tls = nxt_queue_link_data(qlk, nxt_router_tlssock_t, link);
- nxt_router_tls_rpc_create(task, tmcf, tls);
+ nxt_router_tls_rpc_create(task, tmcf, tls,
+ nxt_queue_is_empty(&tmcf->tls));
return;
}
#endif
@@ -990,7 +995,7 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data)
nxt_router_apps_sort(task, router, tmcf);
- nxt_router_apps_hash_use(task, rtcf, 1);
+ nxt_router_apps_hash_use(task, rtcf, 1);
nxt_router_engines_post(router, tmcf);
@@ -1332,6 +1337,9 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_port_t *port;
nxt_router_t *router;
nxt_app_joint_t *app_joint;
+#if (NXT_TLS)
+ nxt_conf_value_t *certificate;
+#endif
nxt_conf_value_t *conf, *http, *value, *websocket;
nxt_conf_value_t *applications, *application;
nxt_conf_value_t *listeners, *listener;
@@ -1343,9 +1351,6 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_router_app_conf_t apcf;
nxt_router_access_log_t *access_log;
nxt_router_listener_conf_t lscf;
-#if (NXT_TLS)
- nxt_router_tlssock_t *tls;
-#endif
static nxt_str_t http_path = nxt_string("/settings/http");
static nxt_str_t applications_path = nxt_string("/applications");
@@ -1729,20 +1734,30 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
}
#if (NXT_TLS)
- value = nxt_conf_get_path(listener, &certificate_path);
+ certificate = nxt_conf_get_path(listener, &certificate_path);
- if (value != NULL) {
- nxt_conf_get_string(value, &name);
+ if (certificate != NULL) {
+ if (nxt_conf_type(certificate) == NXT_CONF_ARRAY) {
+ n = nxt_conf_array_elements_count(certificate);
- tls = nxt_mp_get(mp, sizeof(nxt_router_tlssock_t));
- if (nxt_slow_path(tls == NULL)) {
- goto fail;
- }
+ for (i = 0; i < n; i++) {
+ value = nxt_conf_get_array_element(certificate, i);
- tls->name = name;
- tls->conf = skcf;
+ nxt_assert(value != NULL);
- nxt_queue_insert_tail(&tmcf->tls, &tls->link);
+ ret = nxt_router_conf_tls_insert(tmcf, value, skcf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+ }
+
+ } else {
+ /* NXT_CONF_STRING */
+ ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+ }
}
#endif
@@ -1828,6 +1843,38 @@ fail:
}
+#if (NXT_TLS)
+
+static nxt_int_t
+nxt_router_conf_tls_insert(nxt_router_temp_conf_t *tmcf,
+ nxt_conf_value_t *value, nxt_socket_conf_t *skcf)
+{
+ nxt_mp_t *mp;
+ nxt_str_t str;
+ nxt_router_tlssock_t *tls;
+
+ mp = tmcf->router_conf->mem_pool;
+
+ tls = nxt_mp_get(mp, sizeof(nxt_router_tlssock_t));
+ if (nxt_slow_path(tls == NULL)) {
+ return NXT_ERROR;
+ }
+
+ tls->conf = skcf;
+ nxt_conf_get_string(value, &str);
+
+ if (nxt_slow_path(nxt_str_dup(mp, &tls->name, &str) == NULL)) {
+ return NXT_ERROR;
+ }
+
+ nxt_queue_insert_tail(&tmcf->tls, &tls->link);
+
+ return NXT_OK;
+}
+
+#endif
+
+
static nxt_int_t
nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
nxt_conf_value_t *conf)
@@ -2383,7 +2430,7 @@ nxt_router_listen_socket_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
static void
nxt_router_tls_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
- nxt_router_tlssock_t *tls)
+ nxt_router_tlssock_t *tls, nxt_bool_t last)
{
nxt_socket_rpc_t *rpc;
@@ -2393,8 +2440,10 @@ nxt_router_tls_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
return;
}
+ rpc->name = &tls->name;
rpc->socket_conf = tls->conf;
rpc->temp_conf = tmcf;
+ rpc->last = last;
nxt_cert_store_get(task, &tls->name, tmcf->mem_pool,
nxt_router_tls_rpc_handler, rpc);
@@ -2405,11 +2454,12 @@ static void
nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
{
- nxt_mp_t *mp;
- nxt_int_t ret;
- nxt_tls_conf_t *tlscf;
- nxt_socket_rpc_t *rpc;
- nxt_router_temp_conf_t *tmcf;
+ nxt_mp_t *mp;
+ nxt_int_t ret;
+ nxt_tls_conf_t *tlscf;
+ nxt_socket_rpc_t *rpc;
+ nxt_tls_bundle_conf_t *bundle;
+ nxt_router_temp_conf_t *tmcf;
nxt_debug(task, "tls rpc handler");
@@ -2422,20 +2472,33 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
mp = tmcf->router_conf->mem_pool;
- tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t));
- if (nxt_slow_path(tlscf == NULL)) {
+ if (rpc->socket_conf->tls == NULL){
+ tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t));
+ if (nxt_slow_path(tlscf == NULL)) {
+ goto fail;
+ }
+
+ rpc->socket_conf->tls = tlscf;
+
+ } else {
+ tlscf = rpc->socket_conf->tls;
+ }
+
+ bundle = nxt_mp_get(mp, sizeof(nxt_tls_bundle_conf_t));
+ if (nxt_slow_path(bundle == NULL)) {
goto fail;
}
- tlscf->chain_file = msg->fd[0];
+ bundle->name = rpc->name;
+ bundle->chain_file = msg->fd[0];
+ bundle->next = tlscf->bundle;
+ tlscf->bundle = bundle;
- ret = task->thread->runtime->tls->server_init(task, tlscf);
+ ret = task->thread->runtime->tls->server_init(task, tlscf, mp, rpc->last);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
- rpc->socket_conf->tls = tlscf;
-
nxt_work_queue_add(&task->thread->engine->fast_work_queue,
nxt_router_conf_apply, task, tmcf, NULL);
return;
diff --git a/src/nxt_tls.h b/src/nxt_tls.h
index d9fcc6a8..c44bfe56 100644
--- a/src/nxt_tls.h
+++ b/src/nxt_tls.h
@@ -23,28 +23,47 @@
#define NXT_TLS_BUFFER_SIZE 4096
-typedef struct nxt_tls_conf_s nxt_tls_conf_t;
-
+typedef struct nxt_tls_conf_s nxt_tls_conf_t;
+typedef struct nxt_tls_bundle_conf_s nxt_tls_bundle_conf_t;
typedef struct {
nxt_int_t (*library_init)(nxt_task_t *task);
void (*library_free)(nxt_task_t *task);
nxt_int_t (*server_init)(nxt_task_t *task,
- nxt_tls_conf_t *conf);
+ nxt_tls_conf_t *conf, nxt_mp_t *mp,
+ nxt_bool_t last);
void (*server_free)(nxt_task_t *task,
nxt_tls_conf_t *conf);
} nxt_tls_lib_t;
-struct nxt_tls_conf_s {
+typedef struct {
+ nxt_tls_bundle_conf_t *bundle;
+
+ nxt_str_t name;
+} nxt_tls_bundle_hash_item_t;
+
+
+struct nxt_tls_bundle_conf_s {
void *ctx;
+
+ nxt_fd_t chain_file;
+ nxt_str_t *name;
+
+ nxt_tls_bundle_conf_t *next;
+};
+
+
+struct nxt_tls_conf_s {
+ nxt_tls_bundle_conf_t *bundle;
+ nxt_lvlhsh_t bundle_hash;
+
void (*conn_init)(nxt_task_t *task,
nxt_tls_conf_t *conf, nxt_conn_t *c);
const nxt_tls_lib_t *lib;
- nxt_fd_t chain_file;
char *ciphers;
char *ca_certificate;