summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--docs/changes.xml7
-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
5 files changed, 524 insertions, 68 deletions
diff --git a/docs/changes.xml b/docs/changes.xml
index 009866bb..cca6bbc4 100644
--- a/docs/changes.xml
+++ b/docs/changes.xml
@@ -11,6 +11,13 @@
<change type="feature">
<para>
+support for multiple certificate bundles on a listener via Server Name
+Indication (SNI) TLS extension.
+</para>
+</change>
+
+<change type="feature">
+<para>
"--mandir" ./configure option to specify the directory for man page installation.
</para>
</change>
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;