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.
This commit is contained in:
Andrey Suvorov
2021-03-24 13:19:36 -07:00
parent d62192738f
commit d2b0882d89
5 changed files with 525 additions and 69 deletions

View File

@@ -9,6 +9,13 @@
date="" time="" date="" time=""
packager="Andrei Belov <defan@nginx.com>"> packager="Andrei Belov <defan@nginx.com>">
<change type="feature">
<para>
support for multiple certificate bundles on a listener via Server Name
Indication (SNI) TLS extension.
</para>
</change>
<change type="feature"> <change type="feature">
<para> <para>
"--mandir" ./configure option to specify the directory for man page installation. "--mandir" ./configure option to specify the directory for man page installation.

View File

@@ -87,6 +87,8 @@ static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt,
#if (NXT_TLS) #if (NXT_TLS)
static nxt_int_t nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt, static nxt_int_t nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data); 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 #endif
static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt, static nxt_int_t nxt_conf_vldt_action(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data); 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[] = { static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
{ {
.name = nxt_string("certificate"), .name = nxt_string("certificate"),
.type = NXT_CONF_VLDT_STRING, .type = NXT_CONF_VLDT_STRING | NXT_CONF_VLDT_ARRAY,
.validator = nxt_conf_vldt_certificate, .validator = nxt_conf_vldt_certificate,
}, },
@@ -1826,10 +1828,35 @@ nxt_conf_vldt_match_patterns_set_member(nxt_conf_validation_t *vldt,
static nxt_int_t static nxt_int_t
nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt, nxt_conf_value_t *value, nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data) 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_str_t name;
nxt_conf_value_t *cert; 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); nxt_conf_get_string(value, &name);
cert = nxt_cert_info_get(&name); cert = nxt_cert_info_get(&name);

View File

@@ -9,6 +9,7 @@
#include <openssl/conf.h> #include <openssl/conf.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/rand.h> #include <openssl/rand.h>
#include <openssl/x509v3.h>
typedef struct { typedef struct {
@@ -19,6 +20,7 @@ typedef struct {
uint8_t times; /* 2 bits */ uint8_t times; /* 2 bits */
uint8_t handshake; /* 1 bit */ uint8_t handshake; /* 1 bit */
nxt_tls_conf_t *conf;
nxt_buf_mem_t buffer; nxt_buf_mem_t buffer;
} nxt_openssl_conn_t; } nxt_openssl_conn_t;
@@ -40,8 +42,18 @@ static unsigned long nxt_openssl_thread_id(void);
static void nxt_openssl_locks_free(void); static void nxt_openssl_locks_free(void);
#endif #endif
static nxt_int_t nxt_openssl_server_init(nxt_task_t *task, static nxt_int_t nxt_openssl_server_init(nxt_task_t *task,
nxt_tls_conf_t *conf); nxt_tls_conf_t *conf, nxt_mp_t *mp, nxt_bool_t last);
static nxt_int_t nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd); 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_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, static void nxt_openssl_conn_init(nxt_task_t *task, nxt_tls_conf_t *conf,
nxt_conn_t *c); nxt_conn_t *c);
@@ -245,12 +257,13 @@ nxt_openssl_locks_free(void)
static nxt_int_t 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; SSL_CTX *ctx;
nxt_fd_t fd;
const char *ciphers, *ca_certificate; const char *ciphers, *ca_certificate;
STACK_OF(X509_NAME) *list; STACK_OF(X509_NAME) *list;
nxt_tls_bundle_conf_t *bundle;
ctx = SSL_CTX_new(SSLv23_server_method()); ctx = SSL_CTX_new(SSLv23_server_method());
if (ctx == NULL) { if (ctx == NULL) {
@@ -258,8 +271,10 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
return NXT_ERROR; return NXT_ERROR;
} }
conf->ctx = ctx; bundle = conf->bundle;
conf->conn_init = nxt_openssl_conn_init; nxt_assert(bundle != NULL);
bundle->ctx = ctx;
#ifdef SSL_OP_NO_RENEGOTIATION #ifdef SSL_OP_NO_RENEGOTIATION
/* Renegration is not currently supported. */ /* Renegration is not currently supported. */
@@ -287,11 +302,10 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
#endif #endif
fd = conf->chain_file; if (nxt_openssl_chain_file(task, ctx, conf, mp,
last && bundle->next == NULL)
if (nxt_openssl_chain_file(ctx, fd) != NXT_OK) { != NXT_OK)
nxt_openssl_log_error(task, NXT_LOG_ALERT, {
"nxt_openssl_chain_file() failed");
goto fail; 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); 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; return NXT_OK;
fail: fail:
@@ -366,22 +388,27 @@ fail:
static nxt_int_t 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; BIO *bio;
X509 *cert, *ca; X509 *cert, *ca;
long reason; long reason;
EVP_PKEY *key; EVP_PKEY *key;
nxt_int_t ret; nxt_int_t ret;
nxt_tls_bundle_conf_t *bundle;
ret = NXT_ERROR;
cert = NULL;
bio = BIO_new(BIO_s_fd()); bio = BIO_new(BIO_s_fd());
if (bio == NULL) { 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); cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
if (cert == NULL) { if (cert == NULL) {
@@ -392,6 +419,10 @@ nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd)
goto end; goto end;
} }
if (!single && nxt_openssl_cert_get_names(task, cert, conf, mp) != NXT_OK) {
goto clean;
}
for ( ;; ) { for ( ;; ) {
ca = PEM_read_bio_X509(bio, NULL, NULL, NULL); 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: 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); BIO_free(bio);
X509_free(cert);
return ret; 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 static void
nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf) 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 \ #if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \
&& OPENSSL_VERSION_NUMBER < 0x1010101fL) && 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; c->u.tls = tls;
nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size); nxt_buf_mem_set_size(&tls->buffer, conf->buffer_size);
ctx = conf->ctx; ctx = conf->bundle->ctx;
s = SSL_new(ctx); s = SSL_new(ctx);
if (s == NULL) { 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; tls->session = s;
/* To pass TLS config to the nxt_openssl_servername() callback. */
tls->conf = conf;
tls->conn = c; tls->conn = c;
ret = SSL_set_fd(s, c->socket.fd); 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; c->sendfile = NXT_CONN_SENDFILE_OFF;
nxt_openssl_conn_handshake(task, c, c->socket.data); nxt_openssl_conn_handshake(task, c, c->socket.data);
/*
* TLS configuration might be destroyed after the TLS connection
* is established.
*/
tls->conf = NULL;
return; return;
fail: fail:

View File

@@ -51,8 +51,10 @@ typedef struct {
typedef struct { typedef struct {
nxt_str_t *name;
nxt_socket_conf_t *socket_conf; nxt_socket_conf_t *socket_conf;
nxt_router_temp_conf_t *temp_conf; nxt_router_temp_conf_t *temp_conf;
nxt_bool_t last;
} nxt_socket_rpc_t; } 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); nxt_port_recv_msg_t *msg, void *data);
#if (NXT_TLS) #if (NXT_TLS)
static void nxt_router_tls_rpc_create(nxt_task_t *task, 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, static void nxt_router_tls_rpc_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data); 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 #endif
static void nxt_router_app_rpc_create(nxt_task_t *task, static void nxt_router_app_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_app_t *app); 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) #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); nxt_queue_remove(qlk);
tls = nxt_queue_link_data(qlk, nxt_router_tlssock_t, link); 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; return;
} }
#endif #endif
@@ -1332,6 +1337,9 @@ nxt_router_conf_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf,
nxt_port_t *port; nxt_port_t *port;
nxt_router_t *router; nxt_router_t *router;
nxt_app_joint_t *app_joint; 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 *conf, *http, *value, *websocket;
nxt_conf_value_t *applications, *application; nxt_conf_value_t *applications, *application;
nxt_conf_value_t *listeners, *listener; 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_app_conf_t apcf;
nxt_router_access_log_t *access_log; nxt_router_access_log_t *access_log;
nxt_router_listener_conf_t lscf; 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 http_path = nxt_string("/settings/http");
static nxt_str_t applications_path = nxt_string("/applications"); 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) #if (NXT_TLS)
value = nxt_conf_get_path(listener, &certificate_path); certificate = nxt_conf_get_path(listener, &certificate_path);
if (value != NULL) { if (certificate != NULL) {
nxt_conf_get_string(value, &name); 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)); for (i = 0; i < n; i++) {
if (nxt_slow_path(tls == NULL)) { value = nxt_conf_get_array_element(certificate, i);
nxt_assert(value != NULL);
ret = nxt_router_conf_tls_insert(tmcf, value, skcf);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail; goto fail;
} }
}
tls->name = name; } else {
tls->conf = skcf; /* NXT_CONF_STRING */
ret = nxt_router_conf_tls_insert(tmcf, certificate, skcf);
nxt_queue_insert_tail(&tmcf->tls, &tls->link); if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
}
} }
#endif #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 static nxt_int_t
nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf, nxt_router_conf_process_static(nxt_task_t *task, nxt_router_conf_t *rtcf,
nxt_conf_value_t *conf) 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 static void
nxt_router_tls_rpc_create(nxt_task_t *task, nxt_router_temp_conf_t *tmcf, 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; 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; return;
} }
rpc->name = &tls->name;
rpc->socket_conf = tls->conf; rpc->socket_conf = tls->conf;
rpc->temp_conf = tmcf; rpc->temp_conf = tmcf;
rpc->last = last;
nxt_cert_store_get(task, &tls->name, tmcf->mem_pool, nxt_cert_store_get(task, &tls->name, tmcf->mem_pool,
nxt_router_tls_rpc_handler, rpc); nxt_router_tls_rpc_handler, rpc);
@@ -2409,6 +2458,7 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
nxt_int_t ret; nxt_int_t ret;
nxt_tls_conf_t *tlscf; nxt_tls_conf_t *tlscf;
nxt_socket_rpc_t *rpc; nxt_socket_rpc_t *rpc;
nxt_tls_bundle_conf_t *bundle;
nxt_router_temp_conf_t *tmcf; nxt_router_temp_conf_t *tmcf;
nxt_debug(task, "tls rpc handler"); nxt_debug(task, "tls rpc handler");
@@ -2422,19 +2472,32 @@ nxt_router_tls_rpc_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
mp = tmcf->router_conf->mem_pool; mp = tmcf->router_conf->mem_pool;
if (rpc->socket_conf->tls == NULL){
tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t)); tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t));
if (nxt_slow_path(tlscf == NULL)) { if (nxt_slow_path(tlscf == NULL)) {
goto fail; goto fail;
} }
tlscf->chain_file = msg->fd[0]; rpc->socket_conf->tls = tlscf;
ret = task->thread->runtime->tls->server_init(task, tlscf); } else {
if (nxt_slow_path(ret != NXT_OK)) { tlscf = rpc->socket_conf->tls;
}
bundle = nxt_mp_get(mp, sizeof(nxt_tls_bundle_conf_t));
if (nxt_slow_path(bundle == NULL)) {
goto fail; goto fail;
} }
rpc->socket_conf->tls = tlscf; 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, mp, rpc->last);
if (nxt_slow_path(ret != NXT_OK)) {
goto fail;
}
nxt_work_queue_add(&task->thread->engine->fast_work_queue, nxt_work_queue_add(&task->thread->engine->fast_work_queue,
nxt_router_conf_apply, task, tmcf, NULL); nxt_router_conf_apply, task, tmcf, NULL);

View File

@@ -24,27 +24,46 @@
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 { typedef struct {
nxt_int_t (*library_init)(nxt_task_t *task); nxt_int_t (*library_init)(nxt_task_t *task);
void (*library_free)(nxt_task_t *task); void (*library_free)(nxt_task_t *task);
nxt_int_t (*server_init)(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, void (*server_free)(nxt_task_t *task,
nxt_tls_conf_t *conf); nxt_tls_conf_t *conf);
} nxt_tls_lib_t; } 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; 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, void (*conn_init)(nxt_task_t *task,
nxt_tls_conf_t *conf, nxt_conn_t *c); nxt_tls_conf_t *conf, nxt_conn_t *c);
const nxt_tls_lib_t *lib; const nxt_tls_lib_t *lib;
nxt_fd_t chain_file;
char *ciphers; char *ciphers;
char *ca_certificate; char *ca_certificate;