summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--auto/sources2
-rw-r--r--src/nxt_cert.c1204
-rw-r--r--src/nxt_cert.h32
-rw-r--r--src/nxt_conf.c99
-rw-r--r--src/nxt_conf.h12
-rw-r--r--src/nxt_conf_validation.c52
-rw-r--r--src/nxt_controller.c422
-rw-r--r--src/nxt_main_process.c54
-rw-r--r--src/nxt_main_process.h8
-rw-r--r--src/nxt_openssl.c94
-rw-r--r--src/nxt_port.h6
-rw-r--r--src/nxt_router.c136
-rw-r--r--src/nxt_router.h4
-rw-r--r--src/nxt_runtime.c17
-rw-r--r--src/nxt_runtime.h2
-rw-r--r--src/nxt_tls.h3
16 files changed, 2103 insertions, 44 deletions
diff --git a/auto/sources b/auto/sources
index f57a264d..e7074345 100644
--- a/auto/sources
+++ b/auto/sources
@@ -106,7 +106,7 @@ NXT_LIB_UNIT_SRCS="src/nxt_unit.c"
NXT_LIB_TLS_DEPS="src/nxt_tls.h"
-NXT_LIB_TLS_SRCS=
+NXT_LIB_TLS_SRCS="src/nxt_cert.c"
NXT_LIB_OPENSSL_SRCS="src/nxt_openssl.c"
NXT_LIB_GNUTLS_SRCS="src/nxt_gnutls.c"
NXT_LIB_CYASSL_SRCS="src/nxt_cyassl.c"
diff --git a/src/nxt_cert.c b/src/nxt_cert.c
new file mode 100644
index 00000000..ee258646
--- /dev/null
+++ b/src/nxt_cert.c
@@ -0,0 +1,1204 @@
+
+/*
+ * Copyright (C) Valentin V. Bartenev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#include <nxt_main.h>
+#include <nxt_conf.h>
+#include <nxt_cert.h>
+
+#include <dirent.h>
+
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+#include <openssl/rsa.h>
+#include <openssl/err.h>
+
+
+struct nxt_cert_s {
+ EVP_PKEY *key;
+ nxt_uint_t count;
+ X509 *chain[];
+};
+
+
+typedef struct {
+ nxt_str_t name;
+ nxt_conf_value_t *value;
+ nxt_mp_t *mp;
+} nxt_cert_info_t;
+
+
+typedef struct {
+ nxt_str_t name;
+ nxt_fd_t fd;
+} nxt_cert_item_t;
+
+
+static nxt_cert_t *nxt_cert_fd(nxt_task_t *task, nxt_fd_t fd);
+static nxt_cert_t *nxt_cert_bio(nxt_task_t *task, BIO *bio);
+static int nxt_nxt_cert_pem_suffix(char *pem_str, const char *suffix);
+
+static nxt_conf_value_t *nxt_cert_details(nxt_mp_t *mp, nxt_cert_t *cert);
+static nxt_conf_value_t *nxt_cert_name_details(nxt_mp_t *mp, X509 *x509,
+ nxt_bool_t issuer);
+
+
+static nxt_lvlhsh_t nxt_cert_info;
+
+
+nxt_cert_t *
+nxt_cert_mem(nxt_task_t *task, nxt_buf_mem_t *mbuf)
+{
+ BIO *bio;
+ nxt_cert_t *cert;
+
+ bio = BIO_new_mem_buf(mbuf->pos, nxt_buf_mem_used_size(mbuf));
+ if (nxt_slow_path(bio == NULL)) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT, "BIO_new_mem_buf() failed");
+ return NULL;
+ }
+
+ cert = nxt_cert_bio(task, bio);
+
+ BIO_free(bio);
+
+ return cert;
+}
+
+
+static nxt_cert_t *
+nxt_cert_fd(nxt_task_t *task, nxt_fd_t fd)
+{
+ BIO *bio;
+ nxt_cert_t *cert;
+
+ bio = BIO_new_fd(fd, 0);
+ if (nxt_slow_path(bio == NULL)) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT, "BIO_new_fd() failed");
+ return NULL;
+ }
+
+ cert = nxt_cert_bio(task, bio);
+
+ BIO_free(bio);
+
+ return cert;
+}
+
+
+static nxt_cert_t *
+nxt_cert_bio(nxt_task_t *task, BIO *bio)
+{
+ int ret, suffix, key_id;
+ long length, reason;
+ char *type, *header;
+ X509 *x509;
+ EVP_PKEY *key;
+ nxt_uint_t nalloc;
+ nxt_cert_t *cert, *new_cert;
+ u_char *data;
+ const u_char *data_copy;
+ PKCS8_PRIV_KEY_INFO *p8inf;
+ const EVP_PKEY_ASN1_METHOD *ameth;
+
+ nalloc = 4;
+
+ cert = nxt_zalloc(sizeof(nxt_cert_t) + nalloc * sizeof(X509 *));
+ if (cert == NULL) {
+ return NULL;
+ }
+
+ for ( ;; ) {
+ ret = PEM_read_bio(bio, &type, &header, &data, &length);
+
+ if (ret == 0) {
+ reason = ERR_GET_REASON(ERR_peek_last_error());
+ if (reason != PEM_R_NO_START_LINE) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "PEM_read_bio() failed");
+ goto fail;
+ }
+
+ ERR_clear_error();
+ break;
+ }
+
+ nxt_debug(task, "PEM type: \"%s\"", type);
+
+ key = NULL;
+ x509 = NULL;
+/*
+ EVP_CIPHER_INFO cipher;
+
+ if (PEM_get_EVP_CIPHER_INFO(header, &cipher) != 0) {
+ nxt_alert(task, "encrypted PEM isn't supported");
+ goto done;
+ }
+*/
+ if (nxt_strcmp(type, PEM_STRING_PKCS8) == 0) {
+ nxt_alert(task, "PEM PKCS8 isn't supported");
+ goto done;
+ }
+
+ if (nxt_strcmp(type, PEM_STRING_PKCS8INF) == 0) {
+ data_copy = data;
+
+ p8inf = d2i_PKCS8_PRIV_KEY_INFO(NULL, &data_copy, length);
+
+ if (p8inf == NULL) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "d2i_PKCS8_PRIV_KEY_INFO() failed");
+ goto done;
+ }
+
+ key = EVP_PKCS82PKEY(p8inf);
+
+ PKCS8_PRIV_KEY_INFO_free(p8inf);
+ goto done;
+ }
+
+ suffix = nxt_nxt_cert_pem_suffix(type, PEM_STRING_PKCS8INF);
+
+ if (suffix != 0) {
+
+ ameth = EVP_PKEY_asn1_find_str(NULL, type, suffix);
+ if (ameth == NULL) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "EVP_PKEY_asn1_find_str() failed");
+ goto done;
+ }
+
+ EVP_PKEY_asn1_get0_info(&key_id, NULL, NULL, NULL, NULL, ameth);
+
+ data_copy = data;
+
+ key = d2i_PrivateKey(key_id, NULL, &data_copy, length);
+ goto done;
+ }
+
+ if (nxt_strcmp(type, PEM_STRING_X509) == 0
+ || nxt_strcmp(type, PEM_STRING_X509_OLD) == 0)
+ {
+ data_copy = data;
+
+ x509 = d2i_X509(NULL, &data_copy, length);
+ if (x509 == NULL) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "d2i_X509() failed");
+ }
+
+ goto done;
+ }
+
+ if (nxt_strcmp(type, PEM_STRING_X509_TRUSTED) == 0) {
+ data_copy = data;
+
+ x509 = d2i_X509_AUX(NULL, &data_copy, length);
+ if (x509 == NULL) {
+ nxt_openssl_log_error(task, NXT_LOG_ALERT,
+ "d2i_X509_AUX() failed");
+ }
+
+ goto done;
+ }
+
+ nxt_alert(task, "unsupported PEM type: \"%s\"", type);
+
+ done:
+
+ OPENSSL_free(data);
+ OPENSSL_free(header);
+ OPENSSL_free(type);
+
+ if (key != NULL) {
+ if (cert->key != NULL) {
+ EVP_PKEY_free(key);
+ nxt_alert(task, "multiple private keys in PEM");
+ goto fail;
+ }
+
+ cert->key = key;
+ continue;
+ }
+
+ if (x509 != NULL) {
+
+ if (cert->count == nalloc) {
+ nalloc += 4;
+
+ new_cert = nxt_realloc(cert, sizeof(nxt_cert_t)
+ + nalloc * sizeof(X509 *));
+ if (new_cert == NULL) {
+ X509_free(x509);
+ goto fail;
+ }
+
+ nxt_free(cert);
+ cert = new_cert;
+ }
+
+ cert->chain[cert->count++] = x509;
+ continue;
+ }
+
+ goto fail;
+ }
+
+ if (cert->key == NULL) {
+ nxt_alert(task, "no key found");
+ goto fail;
+ }
+
+ if (cert->count == 0) {
+ nxt_alert(task, "no certificates found");
+ goto fail;
+ }
+
+ return cert;
+
+fail:
+
+ nxt_cert_destroy(cert);
+
+ return NULL;
+}
+
+
+static int
+nxt_nxt_cert_pem_suffix(char *pem_str, const char *suffix)
+{
+ char *p;
+ nxt_uint_t pem_len, suffix_len;
+
+ pem_len = strlen(pem_str);
+ suffix_len = strlen(suffix);
+
+ if (suffix_len + 1 >= pem_len) {
+ return 0;
+ }
+
+ p = pem_str + pem_len - suffix_len;
+
+ if (nxt_strcmp(p, suffix) != 0) {
+ return 0;
+ }
+
+ p--;
+
+ if (*p != ' ') {
+ return 0;
+ }
+
+ return p - pem_str;
+}
+
+
+void
+nxt_cert_destroy(nxt_cert_t *cert)
+{
+ nxt_uint_t i;
+
+ EVP_PKEY_free(cert->key);
+
+ for (i = 0; i != cert->count; i++) {
+ X509_free(cert->chain[i]);
+ }
+
+ nxt_free(cert);
+}
+
+
+
+static nxt_int_t
+nxt_cert_info_hash_test(nxt_lvlhsh_query_t *lhq, void *data)
+{
+ nxt_cert_info_t *info;
+
+ info = data;
+
+ if (nxt_strcasestr_eq(&lhq->key, &info->name)) {
+ return NXT_OK;
+ }
+
+ return NXT_DECLINED;
+}
+
+
+static const nxt_lvlhsh_proto_t nxt_cert_info_hash_proto
+ nxt_aligned(64) =
+{
+ NXT_LVLHSH_DEFAULT,
+ nxt_cert_info_hash_test,
+ nxt_lvlhsh_alloc,
+ nxt_lvlhsh_free,
+};
+
+
+void
+nxt_cert_info_init(nxt_task_t *task, nxt_array_t *certs)
+{
+ uint32_t i;
+ nxt_cert_t *cert;
+ nxt_cert_item_t *items;
+
+ for (items = certs->elts, i = 0; i < certs->nelts; i++) {
+ cert = nxt_cert_fd(task, items[i].fd);
+
+ if (nxt_slow_path(cert == NULL)) {
+ continue;
+ }
+
+ (void) nxt_cert_info_save(&items[i].name, cert);
+
+ nxt_cert_destroy(cert);
+ }
+}
+
+
+nxt_int_t
+nxt_cert_info_save(nxt_str_t *name, nxt_cert_t *cert)
+{
+ nxt_mp_t *mp;
+ nxt_int_t ret;
+ nxt_cert_info_t *info;
+ nxt_conf_value_t *value;
+ nxt_lvlhsh_query_t lhq;
+
+ mp = nxt_mp_create(1024, 128, 256, 32);
+ if (nxt_slow_path(mp == NULL)) {
+ return NXT_ERROR;
+ }
+
+ info = nxt_mp_get(mp, sizeof(nxt_cert_info_t));
+ if (nxt_slow_path(info == NULL)) {
+ goto fail;
+ }
+
+ name = nxt_str_dup(mp, &info->name, name);
+ if (nxt_slow_path(name == NULL)) {
+ goto fail;
+ }
+
+ value = nxt_cert_details(mp, cert);
+ if (nxt_slow_path(value == NULL)) {
+ goto fail;
+ }
+
+ info->mp = mp;
+ info->value = value;
+
+ lhq.key_hash = nxt_djb_hash(name->start, name->length);
+ lhq.replace = 1;
+ lhq.key = *name;
+ lhq.value = info;
+ lhq.proto = &nxt_cert_info_hash_proto;
+
+ ret = nxt_lvlhsh_insert(&nxt_cert_info, &lhq);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+
+ if (lhq.value != info) {
+ info = lhq.value;
+ nxt_mp_destroy(info->mp);
+ }
+
+ return NXT_OK;
+
+fail:
+
+ nxt_mp_destroy(mp);
+ return NXT_ERROR;
+}
+
+
+nxt_conf_value_t *
+nxt_cert_info_get(nxt_str_t *name)
+{
+ nxt_int_t ret;
+ nxt_cert_info_t *info;
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = nxt_djb_hash(name->start, name->length);
+ lhq.key = *name;
+ lhq.proto = &nxt_cert_info_hash_proto;
+
+ ret = nxt_lvlhsh_find(&nxt_cert_info, &lhq);
+ if (ret != NXT_OK) {
+ return NULL;
+ }
+
+ info = lhq.value;
+
+ return info->value;
+}
+
+
+nxt_conf_value_t *
+nxt_cert_info_get_all(nxt_mp_t *mp)
+{
+ uint32_t i;
+ nxt_cert_info_t *info;
+ nxt_conf_value_t *all;
+ nxt_lvlhsh_each_t lhe;
+
+ nxt_lvlhsh_each_init(&lhe, &nxt_cert_info_hash_proto);
+
+ i = 0;
+
+ for ( ;; ) {
+ info = nxt_lvlhsh_each(&nxt_cert_info, &lhe);
+
+ if (info == NULL) {
+ break;
+ }
+
+ i++;
+ }
+
+ all = nxt_conf_create_object(mp, i);
+ if (nxt_slow_path(all == NULL)) {
+ return NULL;
+ }
+
+ nxt_lvlhsh_each_init(&lhe, &nxt_cert_info_hash_proto);
+
+ i = 0;
+
+ for ( ;; ) {
+ info = nxt_lvlhsh_each(&nxt_cert_info, &lhe);
+
+ if (info == NULL) {
+ break;
+ }
+
+ nxt_conf_set_member(all, &info->name, info->value, i);
+
+ i++;
+ }
+
+ return all;
+}
+
+
+static nxt_conf_value_t *
+nxt_cert_details(nxt_mp_t *mp, nxt_cert_t *cert)
+{
+ BIO *bio;
+ X509 *x509;
+ u_char *end;
+ EVP_PKEY *key;
+ ASN1_TIME *asn1_time;
+ nxt_str_t str;
+ nxt_int_t ret;
+ nxt_uint_t i;
+ nxt_conf_value_t *object, *chain, *element, *value;
+ u_char buf[256];
+
+ static nxt_str_t key_str = nxt_string("key");
+ static nxt_str_t chain_str = nxt_string("chain");
+ static nxt_str_t since_str = nxt_string("since");
+ static nxt_str_t until_str = nxt_string("until");
+ static nxt_str_t issuer_str = nxt_string("issuer");
+ static nxt_str_t subject_str = nxt_string("subject");
+ static nxt_str_t validity_str = nxt_string("validity");
+
+ object = nxt_conf_create_object(mp, 2);
+ if (nxt_slow_path(object == NULL)) {
+ return NULL;
+ }
+
+ if (cert->key != NULL) {
+ key = cert->key;
+
+ switch (EVP_PKEY_base_id(key)) {
+ case EVP_PKEY_RSA:
+ end = nxt_sprintf(buf, buf + sizeof(buf), "RSA (%d bits)",
+ EVP_PKEY_bits(key));
+
+ str.length = end - buf;
+ str.start = buf;
+ break;
+
+ case EVP_PKEY_DH:
+ end = nxt_sprintf(buf, buf + sizeof(buf), "DH (%d bits)",
+ EVP_PKEY_bits(key));
+
+ str.length = end - buf;
+ str.start = buf;
+ break;
+
+ case EVP_PKEY_EC:
+ nxt_str_set(&str, "ECDH");
+ break;
+
+ default:
+ nxt_str_set(&str, "unknown");
+ }
+
+ ret = nxt_conf_set_member_string_dup(object, mp, &key_str, &str, 0);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
+ }
+
+ } else {
+ nxt_conf_set_member_null(object, &key_str, 0);
+ }
+
+ chain = nxt_conf_create_array(mp, cert->count);
+ if (nxt_slow_path(chain == NULL)) {
+ return NULL;
+ }
+
+ for (i = 0; i < cert->count; i++) {
+ element = nxt_conf_create_object(mp, 3);
+ if (nxt_slow_path(element == NULL)) {
+ return NULL;
+ }
+
+ x509 = cert->chain[i];
+
+ value = nxt_cert_name_details(mp, x509, 0);
+ if (value == NULL) {
+ return NULL;
+ }
+
+ nxt_conf_set_member(element, &subject_str, value, 0);
+
+ value = nxt_cert_name_details(mp, x509, 1);
+ if (value == NULL) {
+ return NULL;
+ }
+
+ nxt_conf_set_member(element, &issuer_str, value, 1);
+
+ value = nxt_conf_create_object(mp, 2);
+ if (nxt_slow_path(value == NULL)) {
+ return NULL;
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ if (nxt_slow_path(bio == NULL)) {
+ return NULL;
+ }
+
+ asn1_time = X509_get_notBefore(x509);
+
+ ret = ASN1_TIME_print(bio, asn1_time);
+
+ if (nxt_fast_path(ret == 1)) {
+ str.length = BIO_get_mem_data(bio, &str.start);
+ ret = nxt_conf_set_member_string_dup(value, mp, &since_str, &str,
+ 0);
+ } else {
+ ret = NXT_ERROR;
+ }
+
+ BIO_free(bio);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
+ }
+
+ bio = BIO_new(BIO_s_mem());
+ if (nxt_slow_path(bio == NULL)) {
+ return NULL;
+ }
+
+ asn1_time = X509_get_notAfter(x509);
+
+ ret = ASN1_TIME_print(bio, asn1_time);
+
+ if (nxt_fast_path(ret == 1)) {
+ str.length = BIO_get_mem_data(bio, &str.start);
+ ret = nxt_conf_set_member_string_dup(value, mp, &until_str, &str,
+ 1);
+ } else {
+ ret = NXT_ERROR;
+ }
+
+ BIO_free(bio);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NULL;
+ }
+
+ nxt_conf_set_member(element, &validity_str, value, 2);
+
+ nxt_conf_set_element(chain, i, element);
+ }
+
+ nxt_conf_set_member(object, &chain_str, chain, 1);
+
+ return object;
+}
+
+
+typedef struct {
+ int nid;
+ nxt_str_t name;
+} nxt_cert_nid_t;
+
+
+static nxt_conf_value_t *
+nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer)
+{
+ int len;
+ X509_NAME *x509_name;
+ nxt_str_t str;
+ nxt_int_t ret;
+ nxt_uint_t i, n, count;
+ GENERAL_NAME *name;
+ nxt_conf_value_t *object, *names;
+ STACK_OF(GENERAL_NAME) *alt_names;
+ u_char buf[256];
+
+ static nxt_cert_nid_t nids[] = {
+ { NID_commonName, nxt_string("common_name") },
+ { NID_countryName, nxt_string("country") },
+ { NID_stateOrProvinceName, nxt_string("state_or_province") },
+ { NID_localityName, nxt_string("locality") },
+ { NID_organizationName, nxt_string("organization") },
+ { NID_organizationalUnitName, nxt_string("department") },
+ };
+
+ static nxt_str_t alt_names_str = nxt_string("alt_names");
+
+ count = 0;
+
+ x509_name = issuer ? X509_get_issuer_name(x509)
+ : X509_get_subject_name(x509);
+
+ for (n = 0; n != nxt_nitems(nids); n++) {
+
+ if (X509_NAME_get_index_by_NID(x509_name, nids[n].nid, -1) < 0) {
+ continue;
+ }
+
+ count++;
+ }
+
+ alt_names = X509_get_ext_d2i(x509, issuer ? NID_issuer_alt_name
+ : NID_subject_alt_name,
+ NULL, NULL);
+
+ if (alt_names != NULL) {
+ count++;
+ }
+
+ object = nxt_conf_create_object(mp, count);
+ if (nxt_slow_path(object == NULL)) {
+ goto fail;
+ }
+
+ for (n = 0, i = 0; n != nxt_nitems(nids) && i != count; n++) {
+
+ len = X509_NAME_get_text_by_NID(x509_name, nids[n].nid,
+ (char *) buf, sizeof(buf));
+
+ if (len < 0) {
+ continue;
+ }
+
+ if (i == 1 && alt_names != NULL) {
+ i++;
+ }
+
+ str.length = len;
+ str.start = buf;
+
+ ret = nxt_conf_set_member_string_dup(object, mp, &nids[n].name,
+ &str, i++);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+ }
+
+ if (alt_names != NULL) {
+ count = sk_GENERAL_NAME_num(alt_names);
+
+ for (n = 0; n != count; n++) {
+ name = sk_GENERAL_NAME_value(alt_names, n);
+
+ if (name->type != GEN_DNS) {
+ continue;
+ }
+ }
+
+ names = nxt_conf_create_array(mp, n);
+ if (nxt_slow_path(names == NULL)) {
+ goto fail;
+ }
+
+ for (n = 0, i = 0; n != count; n++) {
+ name = sk_GENERAL_NAME_value(alt_names, n);
+
+ 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
+
+ ret = nxt_conf_set_element_string_dup(names, mp, i++, &str);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto fail;
+ }
+ }
+
+ sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
+
+ nxt_conf_set_member(object, &alt_names_str, names, 1);
+ }
+
+ return object;
+
+fail:
+
+ if (alt_names != NULL) {
+ sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free);
+ }
+
+ return NULL;
+}
+
+
+nxt_int_t
+nxt_cert_info_delete(nxt_str_t *name)
+{
+ nxt_int_t ret;
+ nxt_cert_info_t *info;
+ nxt_lvlhsh_query_t lhq;
+
+ lhq.key_hash = nxt_djb_hash(name->start, name->length);
+ lhq.key = *name;
+ lhq.proto = &nxt_cert_info_hash_proto;
+
+ ret = nxt_lvlhsh_delete(&nxt_cert_info, &lhq);
+
+ if (ret == NXT_OK) {
+ info = lhq.value;
+ nxt_mp_destroy(info->mp);
+ }
+
+ return ret;
+}
+
+
+
+nxt_array_t *
+nxt_cert_store_load(nxt_task_t *task)
+{
+ DIR *dir;
+ size_t size, alloc;
+ u_char *buf, *p;
+ nxt_mp_t *mp;
+ nxt_str_t name;
+ nxt_int_t ret;
+ nxt_file_t file;
+ nxt_array_t *certs;
+ nxt_runtime_t *rt;
+ struct dirent *de;
+ nxt_cert_item_t *item;
+
+ rt = task->thread->runtime;
+
+ if (nxt_slow_path(rt->certs.start == NULL)) {
+ nxt_alert(task, "no certificates storage directory");
+ return NULL;
+ }
+
+ mp = nxt_mp_create(1024, 128, 256, 32);
+ if (nxt_slow_path(mp == NULL)) {
+ return NULL;
+ }
+
+ certs = nxt_array_create(mp, 16, sizeof(nxt_cert_item_t));
+ if (nxt_slow_path(certs == NULL)) {
+ nxt_mp_destroy(mp);
+ return NULL;
+ }
+
+ buf = NULL;
+ alloc = 0;
+
+ dir = opendir((char *) rt->certs.start);
+ if (nxt_slow_path(dir == NULL)) {
+ nxt_alert(task, "opendir(\"%s\") failed %E",
+ rt->certs.start, nxt_errno);
+ goto fail;
+ }
+
+ for ( ;; ) {
+ de = readdir(dir);
+ if (de == NULL) {
+ break;
+ }
+
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+
+ item = nxt_array_add(certs);
+ if (nxt_slow_path(item == NULL)) {
+ goto fail;
+ }
+
+ item->fd = -1;
+
+ name.length = nxt_strlen(de->d_name);
+ name.start = (u_char *) de->d_name;
+
+ size = rt->certs.length + name.length + 1;
+
+ if (size > alloc) {
+ size += 32;
+
+ p = nxt_realloc(buf, size);
+ if (p == NULL) {
+ goto fail;
+ }
+
+ alloc = size;
+ buf = p;
+ }
+
+ p = nxt_cpymem(buf, rt->certs.start, rt->certs.length);
+ p = nxt_cpymem(p, name.start, name.length + 1);
+
+ nxt_memzero(&file, sizeof(nxt_file_t));
+
+ file.name = buf;
+
+ ret = nxt_file_open(task, &file, NXT_FILE_RDONLY, NXT_FILE_OPEN,
+ NXT_FILE_OWNER_ACCESS);
+
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_array_remove_last(certs);
+ continue;
+ }
+
+ item->fd = file.fd;
+
+ if (nxt_slow_path(nxt_str_dup(mp, &item->name, &name) == NULL)) {
+ goto fail;
+ }
+ }
+
+ if (buf != NULL) {
+ nxt_free(buf);
+ }
+
+ (void) closedir(dir);
+
+ return certs;
+
+fail:
+
+ if (buf != NULL) {
+ nxt_free(buf);
+ }
+
+ if (dir != NULL) {
+ (void) closedir(dir);
+ }
+
+ nxt_cert_store_release(certs);
+
+ return NULL;
+}
+
+
+void
+nxt_cert_store_release(nxt_array_t *certs)
+{
+ uint32_t i;
+ nxt_cert_item_t *items;
+
+ for (items = certs->elts, i = 0;
+ i < certs->nelts;
+ i++)
+ {
+ nxt_fd_close(items[i].fd);
+ }
+
+ nxt_mp_destroy(certs->mem_pool);
+}
+
+
+#if 0
+
+void
+nxt_cert_store_discovery_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+{
+ DIR *dir;
+ size_t size;
+ nxt_buf_t *b;
+ nxt_int_t ret;
+ nxt_port_t *port;
+ nxt_runtime_t *rt;
+ struct dirent *de;
+
+ port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
+ msg->port_msg.reply_port);
+
+ if (nxt_slow_path(port == NULL)) {
+ return;
+ }
+
+ b = NULL;
+ dir = NULL;
+
+ rt = task->thread->runtime;
+
+ if (nxt_slow_path(rt->certs.start == NULL)) {
+ nxt_alert(task, "no certificates storage directory");
+ goto fail;
+ }
+
+ dir = opendir((char *) rt->certs.start);
+ if (nxt_slow_path(dir == NULL)) {
+ nxt_alert(task, "opendir(\"%s\") failed %E",
+ rt->certs.start, nxt_errno);
+ goto fail;
+ }
+
+ size = 0;
+
+ for ( ;; ) {
+ de = readdir(dir);
+ if (de == NULL) {
+ break;
+ }
+
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+
+ size += nxt_strlen(de->d_name) + 1;
+ }
+
+ b = nxt_port_mmap_get_buf(task, port, size);
+ if (nxt_slow_path(b == NULL)) {
+ goto fail;
+ }
+
+ rewinddir(dir);
+
+ for ( ;; ) {
+ de = readdir(dir);
+ if (de == NULL) {
+ break;
+ }
+
+ if (de->d_type != DT_REG) {
+ continue;
+ }
+
+ size = nxt_strlen(de->d_name) + 1;
+
+ if (nxt_slow_path(size > (size_t) nxt_buf_mem_free_size(&b->mem))) {
+ b->mem.free = b->mem.start;
+ break;
+ }
+
+ b->mem.free = nxt_cpymem(b->mem.free, de->d_name, size);
+ }
+
+ (void) closedir(dir);
+ dir = NULL;
+
+ if (nxt_slow_path(nxt_buf_mem_free_size(&b->mem) != 0)) {
+ nxt_alert(task, "certificates storage directory "
+ "has changed while reading it");
+ goto fail;
+ }
+
+ ret = nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_READY_LAST, -1,
+ msg->port_msg.stream, 0, b);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ return;
+ }
+
+fail:
+
+ if (dir != NULL) {
+ (void) closedir(dir);
+ }
+
+ if (b != NULL) {
+ b->completion_handler(task, b, b->parent);
+ }
+
+ (void) nxt_port_socket_write(task, port, NXT_PORT_MSG_RPC_ERROR, -1,
+ msg->port_msg.stream, 0, NULL);
+}
+
+#endif
+
+
+void
+nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
+ nxt_port_rpc_handler_t handler, void *ctx)
+{
+ uint32_t stream;
+ nxt_int_t ret;
+ nxt_buf_t *b;
+ nxt_port_t *main_port, *recv_port;
+ nxt_runtime_t *rt;
+
+ b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
+ if (nxt_slow_path(b == NULL)) {
+ goto fail;
+ }
+
+ nxt_buf_cpystr(b, name);
+ *b->mem.free++ = '\0';
+
+ rt = task->thread->runtime;
+ main_port = rt->port_by_type[NXT_PROCESS_MAIN];
+ recv_port = rt->port_by_type[rt->type];
+
+ stream = nxt_port_rpc_register_handler(task, recv_port, handler, handler,
+ -1, ctx);
+ if (nxt_slow_path(stream == 0)) {
+ goto fail;
+ }
+
+ ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_GET, -1,
+ stream, recv_port->id, b);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ nxt_port_rpc_cancel(task, recv_port, stream);
+ goto fail;
+ }
+
+ return;
+
+fail:
+
+ handler(task, NULL, ctx);
+}
+
+
+void
+nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+{
+ u_char *p;
+ nxt_int_t ret;
+ nxt_str_t name;
+ nxt_file_t file;
+ nxt_port_t *port;
+ nxt_runtime_t *rt;
+ nxt_port_msg_type_t type;
+
+ port = nxt_runtime_port_find(task->thread->runtime, msg->port_msg.pid,
+ msg->port_msg.reply_port);
+
+ if (port == NULL) {
+ return;
+ }
+
+ nxt_memzero(&file, sizeof(nxt_file_t));
+
+ file.fd = -1;
+ type = NXT_PORT_MSG_RPC_ERROR;
+
+ rt = task->thread->runtime;
+
+ if (nxt_slow_path(rt->certs.start == NULL)) {
+ nxt_alert(task, "no certificates storage directory");
+ goto error;
+ }
+
+ name.start = msg->buf->mem.pos;
+ name.length = nxt_strlen(name.start);
+
+ file.name = nxt_malloc(rt->certs.length + name.length + 1);
+
+ if (nxt_slow_path(file.name == NULL)) {
+ goto error;
+ }
+
+ p = nxt_cpymem(file.name, rt->certs.start, rt->certs.length);
+ p = nxt_cpymem(p, name.start, name.length + 1);
+
+ ret = nxt_file_open(task, &file, NXT_FILE_RDWR, NXT_FILE_CREATE_OR_OPEN,
+ NXT_FILE_OWNER_ACCESS);
+
+ nxt_free(file.name);
+
+ if (nxt_fast_path(ret == NXT_OK)) {
+ type = NXT_PORT_MSG_RPC_READY_LAST | NXT_PORT_MSG_CLOSE_FD;
+ }
+
+error:
+
+ (void) nxt_port_socket_write(task, port, type, file.fd,
+ msg->port_msg.stream, 0, NULL);
+}
+
+
+void
+nxt_cert_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp)
+{
+ nxt_buf_t *b;
+ nxt_port_t *main_port;
+ nxt_runtime_t *rt;
+
+ b = nxt_buf_mem_alloc(mp, name->length + 1, 0);
+
+ if (nxt_fast_path(b != NULL)) {
+ nxt_buf_cpystr(b, name);
+ *b->mem.free++ = '\0';
+
+ rt = task->thread->runtime;
+ main_port = rt->port_by_type[NXT_PROCESS_MAIN];
+
+ (void) nxt_port_socket_write(task, main_port, NXT_PORT_MSG_CERT_DELETE,
+ -1, 0, 0, b);
+ }
+}
+
+
+void
+nxt_cert_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
+{
+ u_char *p;
+ nxt_str_t name;
+ nxt_runtime_t *rt;
+ nxt_file_name_t *path;
+
+ rt = task->thread->runtime;
+
+ if (nxt_slow_path(rt->certs.start == NULL)) {
+ nxt_alert(task, "no certificates storage directory");
+ return;
+ }
+
+ name.start = msg->buf->mem.pos;
+ name.length = nxt_strlen(name.start);
+
+ path = nxt_malloc(rt->certs.length + name.length + 1);
+
+ if (nxt_fast_path(path != NULL)) {
+ p = nxt_cpymem(path, rt->certs.start, rt->certs.length);
+ p = nxt_cpymem(p, name.start, name.length + 1);
+
+ (void) nxt_file_delete(path);
+
+ nxt_free(path);
+ }
+}
diff --git a/src/nxt_cert.h b/src/nxt_cert.h
new file mode 100644
index 00000000..319d5d3c
--- /dev/null
+++ b/src/nxt_cert.h
@@ -0,0 +1,32 @@
+
+/*
+ * Copyright (C) Valentin V. Bartenev
+ * Copyright (C) NGINX, Inc.
+ */
+
+#ifndef _NXT_CERT_INCLUDED_
+#define _NXT_CERT_INCLUDED_
+
+
+typedef struct nxt_cert_s nxt_cert_t;
+
+nxt_cert_t *nxt_cert_mem(nxt_task_t *task, nxt_buf_mem_t *mbuf);
+void nxt_cert_destroy(nxt_cert_t *cert);
+
+void nxt_cert_info_init(nxt_task_t *task, nxt_array_t *certs);
+nxt_int_t nxt_cert_info_save(nxt_str_t *name, nxt_cert_t *cert);
+nxt_conf_value_t *nxt_cert_info_get(nxt_str_t *name);
+nxt_conf_value_t *nxt_cert_info_get_all(nxt_mp_t *mp);
+nxt_int_t nxt_cert_info_delete(nxt_str_t *name);
+
+nxt_array_t *nxt_cert_store_load(nxt_task_t *task);
+void nxt_cert_store_release(nxt_array_t *certs);
+
+void nxt_cert_store_get(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp,
+ nxt_port_rpc_handler_t handler, void *ctx);
+void nxt_cert_store_delete(nxt_task_t *task, nxt_str_t *name, nxt_mp_t *mp);
+
+void nxt_cert_store_get_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg);
+void nxt_cert_store_delete_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg);
+
+#endif /* _NXT_CERT_INCLUDED_ */
diff --git a/src/nxt_conf.c b/src/nxt_conf.c
index 1bd82f58..58eb2080 100644
--- a/src/nxt_conf.c
+++ b/src/nxt_conf.c
@@ -180,6 +180,33 @@ nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str)
}
+nxt_int_t
+nxt_conf_set_string_dup(nxt_conf_value_t *value, nxt_mp_t *mp, nxt_str_t *str)
+{
+ nxt_str_t tmp, *ptr;
+
+ if (str->length > NXT_CONF_MAX_SHORT_STRING) {
+ value->type = NXT_CONF_VALUE_STRING;
+
+ ptr = nxt_str_dup(mp, &tmp, str);
+ if (nxt_slow_path(ptr == NULL)) {
+ return NXT_ERROR;
+ }
+
+ value->u.string.length = tmp.length;
+ value->u.string.start = tmp.start;
+
+ } else {
+ value->type = NXT_CONF_VALUE_SHORT_STRING;
+ value->u.str.length = str->length;
+
+ nxt_memcpy(value->u.str.start, str->start, str->length);
+ }
+
+ return NXT_OK;
+}
+
+
int64_t
nxt_conf_get_integer(nxt_conf_value_t *value)
{
@@ -246,6 +273,20 @@ nxt_conf_set_member_string(nxt_conf_value_t *object, nxt_str_t *name,
}
+nxt_int_t
+nxt_conf_set_member_string_dup(nxt_conf_value_t *object, nxt_mp_t *mp,
+ nxt_str_t *name, nxt_str_t *value, uint32_t index)
+{
+ nxt_conf_object_member_t *member;
+
+ member = &object->u.object->members[index];
+
+ nxt_conf_set_string(&member->name, name);
+
+ return nxt_conf_set_string_dup(&member->value, mp, value);
+}
+
+
void
nxt_conf_set_member_integer(nxt_conf_value_t *object, nxt_str_t *name,
int64_t value, uint32_t index)
@@ -261,6 +302,64 @@ nxt_conf_set_member_integer(nxt_conf_value_t *object, nxt_str_t *name,
}
+void
+nxt_conf_set_member_null(nxt_conf_value_t *object, nxt_str_t *name,
+ uint32_t index)
+{
+ nxt_conf_object_member_t *member;
+
+ member = &object->u.object->members[index];
+
+ nxt_conf_set_string(&member->name, name);
+
+ member->value.type = NXT_CONF_VALUE_NULL;
+}
+
+
+nxt_conf_value_t *
+nxt_conf_create_array(nxt_mp_t *mp, nxt_uint_t count)
+{
+ size_t size;
+ nxt_conf_value_t *value;
+
+ size = sizeof(nxt_conf_value_t)
+ + sizeof(nxt_conf_array_t)
+ + count * sizeof(nxt_conf_value_t);
+
+ value = nxt_mp_get(mp, size);
+ if (nxt_slow_path(value == NULL)) {
+ return NULL;
+ }
+
+ value->u.array = nxt_pointer_to(value, sizeof(nxt_conf_value_t));
+ value->u.array->count = count;
+
+ value->type = NXT_CONF_VALUE_ARRAY;
+
+ return value;
+}
+
+
+void
+nxt_conf_set_element(nxt_conf_value_t *array, nxt_uint_t index,
+ nxt_conf_value_t *value)
+{
+ array->u.array->elements[index] = *value;
+}
+
+
+nxt_int_t
+nxt_conf_set_element_string_dup(nxt_conf_value_t *array, nxt_mp_t *mp,
+ nxt_uint_t index, nxt_str_t *value)
+{
+ nxt_conf_value_t *element;
+
+ element = &array->u.array->elements[index];
+
+ return nxt_conf_set_string_dup(element, mp, value);
+}
+
+
nxt_uint_t
nxt_conf_type(nxt_conf_value_t *value)
{
diff --git a/src/nxt_conf.h b/src/nxt_conf.h
index 3ddff801..48521917 100644
--- a/src/nxt_conf.h
+++ b/src/nxt_conf.h
@@ -102,6 +102,8 @@ nxt_int_t nxt_conf_validate(nxt_conf_validation_t *vldt);
NXT_EXPORT void nxt_conf_get_string(nxt_conf_value_t *value, nxt_str_t *str);
NXT_EXPORT void nxt_conf_set_string(nxt_conf_value_t *value, nxt_str_t *str);
+NXT_EXPORT nxt_int_t nxt_conf_set_string_dup(nxt_conf_value_t *value,
+ nxt_mp_t *mp, nxt_str_t *str);
NXT_EXPORT int64_t nxt_conf_get_integer(nxt_conf_value_t *value);
// FIXME reimplement and reorder functions below
@@ -111,8 +113,18 @@ void nxt_conf_set_member(nxt_conf_value_t *object, nxt_str_t *name,
nxt_conf_value_t *value, uint32_t index);
void nxt_conf_set_member_string(nxt_conf_value_t *object, nxt_str_t *name,
nxt_str_t *value, uint32_t index);
+nxt_int_t nxt_conf_set_member_string_dup(nxt_conf_value_t *object, nxt_mp_t *mp,
+ nxt_str_t *name, nxt_str_t *value, uint32_t index);
void nxt_conf_set_member_integer(nxt_conf_value_t *object, nxt_str_t *name,
int64_t value, uint32_t index);
+void nxt_conf_set_member_null(nxt_conf_value_t *object, nxt_str_t *name,
+ uint32_t index);
+
+nxt_conf_value_t *nxt_conf_create_array(nxt_mp_t *mp, nxt_uint_t count);
+void nxt_conf_set_element(nxt_conf_value_t *array, nxt_uint_t index,
+ nxt_conf_value_t *value);
+nxt_int_t nxt_conf_set_element_string_dup(nxt_conf_value_t *array, nxt_mp_t *mp,
+ nxt_uint_t index, nxt_str_t *value);
#endif /* _NXT_CONF_INCLUDED_ */
diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c
index d51ff0ef..ca7107cc 100644
--- a/src/nxt_conf_validation.c
+++ b/src/nxt_conf_validation.c
@@ -6,6 +6,7 @@
#include <nxt_main.h>
#include <nxt_conf.h>
+#include <nxt_cert.h>
#include <nxt_router.h>
@@ -49,6 +50,10 @@ static nxt_int_t nxt_conf_vldt_error(nxt_conf_validation_t *vldt,
static nxt_int_t nxt_conf_vldt_listener(nxt_conf_validation_t *vldt,
nxt_str_t *name, nxt_conf_value_t *value);
+#if (NXT_TLS)
+static nxt_int_t nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt,
+ nxt_conf_value_t *value, void *data);
+#endif
static nxt_int_t nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt,
nxt_conf_value_t *value, void *data);
static nxt_int_t nxt_conf_vldt_app(nxt_conf_validation_t *vldt,
@@ -138,12 +143,35 @@ static nxt_conf_vldt_object_t nxt_conf_vldt_root_members[] = {
};
+#if (NXT_TLS)
+
+static nxt_conf_vldt_object_t nxt_conf_vldt_tls_members[] = {
+ { nxt_string("certificate"),
+ NXT_CONF_VLDT_STRING,
+ &nxt_conf_vldt_certificate,
+ NULL },
+
+ NXT_CONF_VLDT_END
+};
+
+#endif
+
+
static nxt_conf_vldt_object_t nxt_conf_vldt_listener_members[] = {
{ nxt_string("application"),
NXT_CONF_VLDT_STRING,
&nxt_conf_vldt_app_name,
NULL },
+#if (NXT_TLS)
+
+ { nxt_string("tls"),
+ NXT_CONF_VLDT_OBJECT,
+ &nxt_conf_vldt_object,
+ (void *) &nxt_conf_vldt_tls_members },
+
+#endif
+
NXT_CONF_VLDT_END
};
@@ -467,6 +495,30 @@ nxt_conf_vldt_listener(nxt_conf_validation_t *vldt, nxt_str_t *name,
}
+#if (NXT_TLS)
+
+static nxt_int_t
+nxt_conf_vldt_certificate(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
+ void *data)
+{
+ nxt_str_t name;
+ nxt_conf_value_t *cert;
+
+ nxt_conf_get_string(value, &name);
+
+ cert = nxt_cert_info_get(&name);
+
+ if (cert == NULL) {
+ return nxt_conf_vldt_error(vldt, "Certificate \"%V\" is not found.",
+ &name);
+ }
+
+ return NXT_OK;
+}
+
+#endif
+
+
static nxt_int_t
nxt_conf_vldt_app_name(nxt_conf_validation_t *vldt, nxt_conf_value_t *value,
void *data)
diff --git a/src/nxt_controller.c b/src/nxt_controller.c
index 4b8d009b..28f0425b 100644
--- a/src/nxt_controller.c
+++ b/src/nxt_controller.c
@@ -9,6 +9,7 @@
#include <nxt_runtime.h>
#include <nxt_main_process.h>
#include <nxt_conf.h>
+#include <nxt_cert.h>
typedef struct {
@@ -72,6 +73,15 @@ static nxt_int_t nxt_controller_request_content_length(void *ctx,
static void nxt_controller_process_request(nxt_task_t *task,
nxt_controller_request_t *req);
+static void nxt_controller_process_config(nxt_task_t *task,
+ nxt_controller_request_t *req, nxt_str_t *path);
+#if (NXT_TLS)
+static void nxt_controller_process_cert(nxt_task_t *task,
+ nxt_controller_request_t *req, nxt_str_t *path);
+static void nxt_controller_process_cert_save(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg, void *data);
+static nxt_bool_t nxt_controller_cert_in_use(nxt_str_t *name);
+#endif
static void nxt_controller_conf_handler(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
static void nxt_controller_conf_store(nxt_task_t *task,
@@ -124,6 +134,7 @@ nxt_controller_start(nxt_task_t *task, void *data)
nxt_conf_value_t *conf;
nxt_event_engine_t *engine;
nxt_conf_validation_t vldt;
+ nxt_controller_init_t *init;
rt = task->thread->runtime;
@@ -144,9 +155,20 @@ nxt_controller_start(nxt_task_t *task, void *data)
nxt_queue_init(&nxt_controller_waiting_requests);
- json = data;
+ init = data;
+
+#if (NXT_TLS)
+
+ if (init->certs != NULL) {
+ nxt_cert_info_init(task, init->certs);
+ nxt_cert_store_release(init->certs);
+ }
- if (json->length == 0) {
+#endif
+
+ json = &init->conf;
+
+ if (json->start == NULL) {
return NXT_OK;
}
@@ -790,42 +812,147 @@ nxt_controller_request_content_length(void *ctx, nxt_http_field_t *field,
static void
nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req)
{
- nxt_mp_t *mp;
- nxt_int_t rc;
+ uint32_t i, count;
nxt_str_t path;
nxt_conn_t *c;
- nxt_buf_mem_t *mbuf;
- nxt_conf_op_t *ops;
nxt_conf_value_t *value;
- nxt_conf_validation_t vldt;
- nxt_conf_json_error_t error;
nxt_controller_response_t resp;
+#if (NXT_TLS)
+ nxt_conf_value_t *certs;
- static const nxt_str_t empty_obj = nxt_string("{}");
+ static nxt_str_t certificates = nxt_string("certificates");
+#endif
+ static nxt_str_t config = nxt_string("config");
c = req->conn;
path = req->parser.path;
- if (nxt_str_start(&path, "/config", 7)) {
+ if (path.length > 1 && path.start[path.length - 1] == '/') {
+ path.length--;
+ }
+ if (nxt_str_start(&path, "/config", 7)
+ && (path.length == 7 || path.start[7] == '/'))
+ {
if (path.length == 7) {
path.length = 1;
- } else if (path.start[7] == '/') {
+ } else {
path.length -= 7;
path.start += 7;
}
+
+ nxt_controller_process_config(task, req, &path);
+ return;
}
- if (path.length > 1 && path.start[path.length - 1] == '/') {
- path.length--;
+#if (NXT_TLS)
+
+ if (nxt_str_start(&path, "/certificates", 13)
+ && (path.length == 13 || path.start[13] == '/'))
+ {
+ if (path.length == 13) {
+ path.length = 1;
+
+ } else {
+ path.length -= 13;
+ path.start += 13;
+ }
+
+ nxt_controller_process_cert(task, req, &path);
+ return;
}
+#endif
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ if (path.length == 1 && path.start[0] == '/') {
+
+ if (!nxt_str_eq(&req->parser.method, "GET", 3)) {
+ goto invalid_method;
+ }
+
+ count = 1;
+#if (NXT_TLS)
+ count++;
+#endif
+
+ value = nxt_conf_create_object(c->mem_pool, count);
+ if (nxt_slow_path(value == NULL)) {
+ goto alloc_fail;
+ }
+
+ i = 0;
+
+#if (NXT_TLS)
+ certs = nxt_cert_info_get_all(c->mem_pool);
+ if (nxt_slow_path(certs == NULL)) {
+ goto alloc_fail;
+ }
+
+ nxt_conf_set_member(value, &certificates, certs, i++);
+#endif
+
+ nxt_conf_set_member(value, &config, nxt_controller_conf.root, i);
+
+ resp.status = 200;
+ resp.conf = value;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+ }
+
+ resp.status = 404;
+ resp.title = (u_char *) "Value doesn't exist.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+invalid_method:
+
+ resp.status = 405;
+ resp.title = (u_char *) "Invalid method.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+alloc_fail:
+
+ resp.status = 500;
+ resp.title = (u_char *) "Memory allocation failed.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+}
+
+
+static void
+nxt_controller_process_config(nxt_task_t *task, nxt_controller_request_t *req,
+ nxt_str_t *path)
+{
+ nxt_mp_t *mp;
+ nxt_int_t rc;
+ nxt_conn_t *c;
+ nxt_buf_mem_t *mbuf;
+ nxt_conf_op_t *ops;
+ nxt_conf_value_t *value;
+ nxt_conf_validation_t vldt;
+ nxt_conf_json_error_t error;
+ nxt_controller_response_t resp;
+
+ static const nxt_str_t empty_obj = nxt_string("{}");
+
nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+ c = req->conn;
+
if (nxt_str_eq(&req->parser.method, "GET", 3)) {
- value = nxt_conf_get_path(nxt_controller_conf.root, &path);
+ value = nxt_conf_get_path(nxt_controller_conf.root, path);
if (value == NULL) {
goto not_found;
@@ -877,10 +1004,10 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req)
return;
}
- if (path.length != 1) {
+ if (path->length != 1) {
rc = nxt_conf_op_compile(c->mem_pool, &ops,
nxt_controller_conf.root,
- &path, value);
+ path, value);
if (rc != NXT_OK) {
nxt_mp_destroy(mp);
@@ -948,7 +1075,7 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req)
return;
}
- if (path.length == 1) {
+ if (path->length == 1) {
mp = nxt_mp_create(1024, 128, 256, 32);
if (nxt_slow_path(mp == NULL)) {
@@ -960,7 +1087,7 @@ nxt_controller_process_request(nxt_task_t *task, nxt_controller_request_t *req)
} else {
rc = nxt_conf_op_compile(c->mem_pool, &ops,
nxt_controller_conf.root,
- &path, NULL);
+ path, NULL);
if (rc != NXT_OK) {
if (rc == NXT_DECLINED) {
@@ -1070,6 +1197,265 @@ no_router:
}
+#if (NXT_TLS)
+
+static void
+nxt_controller_process_cert(nxt_task_t *task,
+ nxt_controller_request_t *req, nxt_str_t *path)
+{
+ u_char *p;
+ nxt_str_t name;
+ nxt_int_t ret;
+ nxt_conn_t *c;
+ nxt_cert_t *cert;
+ nxt_conf_value_t *value;
+ nxt_controller_response_t resp;
+
+ name.length = path->length - 1;
+ name.start = path->start + 1;
+
+ p = nxt_memchr(name.start, '/', name.length);
+
+ if (p != NULL) {
+ name.length = p - name.start;
+
+ path->length -= p - path->start;
+ path->start = p;
+
+ } else {
+ path = NULL;
+ }
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ c = req->conn;
+
+ if (nxt_str_eq(&req->parser.method, "GET", 3)) {
+
+ if (name.length != 0) {
+ value = nxt_cert_info_get(&name);
+ if (value == NULL) {
+ goto cert_not_found;
+ }
+
+ if (path != NULL) {
+ value = nxt_conf_get_path(value, path);
+ if (value == NULL) {
+ goto not_found;
+ }
+ }
+
+ } else {
+ value = nxt_cert_info_get_all(c->mem_pool);
+ if (value == NULL) {
+ goto alloc_fail;
+ }
+ }
+
+ resp.status = 200;
+ resp.conf = value;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+ }
+
+ if (name.length == 0 || path != NULL) {
+ goto invalid_name;
+ }
+
+ if (nxt_str_eq(&req->parser.method, "PUT", 3)) {
+ value = nxt_cert_info_get(&name);
+ if (value != NULL) {
+ goto exists_cert;
+ }
+
+ cert = nxt_cert_mem(task, &c->read->mem);
+ if (cert == NULL) {
+ goto invalid_cert;
+ }
+
+ ret = nxt_cert_info_save(&name, cert);
+
+ nxt_cert_destroy(cert);
+
+ if (nxt_slow_path(ret != NXT_OK)) {
+ goto alloc_fail;
+ }
+
+ nxt_cert_store_get(task, &name, c->mem_pool,
+ nxt_controller_process_cert_save, req);
+ return;
+ }
+
+ if (nxt_str_eq(&req->parser.method, "DELETE", 6)) {
+
+ if (nxt_controller_cert_in_use(&name)) {
+ goto cert_in_use;
+ }
+
+ if (nxt_cert_info_delete(&name) != NXT_OK) {
+ goto cert_not_found;
+ }
+
+ nxt_cert_store_delete(task, &name, c->mem_pool);
+
+ resp.status = 200;
+ resp.title = (u_char *) "Certificate deleted.";
+
+ nxt_controller_response(task, req, &resp);
+ return;
+ }
+
+ resp.status = 405;
+ resp.title = (u_char *) "Invalid method.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+invalid_name:
+
+ resp.status = 400;
+ resp.title = (u_char *) "Invalid certificate name.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+invalid_cert:
+
+ resp.status = 400;
+ resp.title = (u_char *) "Invalid certificate.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+exists_cert:
+
+ resp.status = 400;
+ resp.title = (u_char *) "Certificate already exists.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+cert_in_use:
+
+ resp.status = 400;
+ resp.title = (u_char *) "Certificate is used in the configuration.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+cert_not_found:
+
+ resp.status = 404;
+ resp.title = (u_char *) "Certificate doesn't exist.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+not_found:
+
+ resp.status = 404;
+ resp.title = (u_char *) "Invalid path.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+
+alloc_fail:
+
+ resp.status = 500;
+ resp.title = (u_char *) "Memory allocation failed.";
+ resp.offset = -1;
+
+ nxt_controller_response(task, req, &resp);
+ return;
+}
+
+
+static void
+nxt_controller_process_cert_save(nxt_task_t *task, nxt_port_recv_msg_t *msg,
+ void *data)
+{
+ nxt_conn_t *c;
+ nxt_buf_mem_t *mbuf;
+ nxt_controller_request_t *req;
+ nxt_controller_response_t resp;
+
+ req = data;
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ if (msg == NULL || msg->port_msg.type == _NXT_PORT_MSG_RPC_ERROR) {
+ resp.status = 500;
+ resp.title = (u_char *) "Failed to store certificate.";
+
+ nxt_controller_response(task, req, &resp);
+ return;
+ }
+
+ c = req->conn;
+
+ mbuf = &c->read->mem;
+
+ nxt_fd_write(msg->fd, mbuf->pos, nxt_buf_mem_used_size(mbuf));
+
+ nxt_fd_close(msg->fd);
+
+ nxt_memzero(&resp, sizeof(nxt_controller_response_t));
+
+ resp.status = 200;
+ resp.title = (u_char *) "Certificate chain uploaded.";
+
+ nxt_controller_response(task, req, &resp);
+}
+
+
+static nxt_bool_t
+nxt_controller_cert_in_use(nxt_str_t *name)
+{
+ uint32_t next;
+ nxt_str_t str;
+ nxt_conf_value_t *listeners, *listener, *value;
+
+ static nxt_str_t listeners_path = nxt_string("/listeners");
+ static nxt_str_t certificate_path = nxt_string("/tls/certificate");
+
+ listeners = nxt_conf_get_path(nxt_controller_conf.root, &listeners_path);
+
+ if (listeners != NULL) {
+ next = 0;
+
+ for ( ;; ) {
+ listener = nxt_conf_next_object_member(listeners, &str, &next);
+ if (listener == NULL) {
+ break;
+ }
+
+ value = nxt_conf_get_path(listener, &certificate_path);
+ if (value == NULL) {
+ continue;
+ }
+
+ nxt_conf_get_string(value, &str);
+
+ if (nxt_strstr_eq(&str, name)) {
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+
static void
nxt_controller_conf_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg,
void *data)
diff --git a/src/nxt_main_process.c b/src/nxt_main_process.c
index 0ecac06a..37e09911 100644
--- a/src/nxt_main_process.c
+++ b/src/nxt_main_process.c
@@ -10,6 +10,9 @@
#include <nxt_main_process.h>
#include <nxt_conf.h>
#include <nxt_router.h>
+#if (NXT_TLS)
+#include <nxt_cert.h>
+#endif
typedef struct {
@@ -336,6 +339,10 @@ static nxt_port_handlers_t nxt_main_process_port_handlers = {
.socket = nxt_main_port_socket_handler,
.modules = nxt_main_port_modules_handler,
.conf_store = nxt_main_port_conf_store_handler,
+#if (NXT_TLS)
+ .cert_get = nxt_cert_store_get_handler,
+ .cert_delete = nxt_cert_store_delete_handler,
+#endif
.access_log = nxt_main_port_access_log_handler,
.rpc_ready = nxt_port_rpc_handler,
.rpc_error = nxt_port_rpc_handler,
@@ -439,13 +446,16 @@ static nxt_int_t
nxt_main_create_controller_process(nxt_task_t *task, nxt_runtime_t *rt,
nxt_process_init_t *init)
{
- ssize_t n;
- nxt_int_t ret;
- nxt_str_t conf;
- nxt_file_t file;
- nxt_file_info_t fi;
+ ssize_t n;
+ nxt_int_t ret;
+ nxt_str_t *conf;
+ nxt_file_t file;
+ nxt_file_info_t fi;
+ nxt_controller_init_t ctrl_init;
- conf.length = 0;
+ nxt_memzero(&ctrl_init, sizeof(nxt_controller_init_t));
+
+ conf = &ctrl_init.conf;
nxt_memzero(&file, sizeof(nxt_file_t));
@@ -457,19 +467,19 @@ nxt_main_create_controller_process(nxt_task_t *task, nxt_runtime_t *rt,
ret = nxt_file_info(&file, &fi);
if (nxt_fast_path(ret == NXT_OK && nxt_is_file(&fi))) {
- conf.length = nxt_file_size(&fi);
- conf.start = nxt_malloc(conf.length);
+ conf->length = nxt_file_size(&fi);
+ conf->start = nxt_malloc(conf->length);
- if (nxt_slow_path(conf.start == NULL)) {
+ if (nxt_slow_path(conf->start == NULL)) {
nxt_file_close(task, &file);
return NXT_ERROR;
}
- n = nxt_file_read(&file, conf.start, conf.length, 0);
+ n = nxt_file_read(&file, conf->start, conf->length, 0);
- if (nxt_slow_path(n != (ssize_t) conf.length)) {
- conf.length = 0;
- nxt_free(conf.start);
+ if (nxt_slow_path(n != (ssize_t) conf->length)) {
+ nxt_free(conf->start);
+ conf->start = NULL;
nxt_alert(task, "failed to restore previous configuration: "
"cannot read the file");
@@ -479,12 +489,24 @@ nxt_main_create_controller_process(nxt_task_t *task, nxt_runtime_t *rt,
nxt_file_close(task, &file);
}
- init->data = &conf;
+#if (NXT_TLS)
+ ctrl_init.certs = nxt_cert_store_load(task);
+#endif
+
+ init->data = &ctrl_init;
ret = nxt_main_create_worker_process(task, rt, init);
- if (ret == NXT_OK && conf.length != 0) {
- nxt_free(conf.start);
+ if (ret == NXT_OK) {
+ if (conf->start != NULL) {
+ nxt_free(conf->start);
+ }
+
+#if (NXT_TLS)
+ if (ctrl_init.certs != NULL) {
+ nxt_cert_store_release(ctrl_init.certs);
+ }
+#endif
}
return ret;
diff --git a/src/nxt_main_process.h b/src/nxt_main_process.h
index 9d74787e..d932e11f 100644
--- a/src/nxt_main_process.h
+++ b/src/nxt_main_process.h
@@ -19,6 +19,14 @@ typedef enum {
} nxt_socket_error_t;
+typedef struct {
+ nxt_str_t conf;
+#if (NXT_TLS)
+ nxt_array_t *certs;
+#endif
+} nxt_controller_init_t;
+
+
nxt_int_t nxt_main_process_start(nxt_thread_t *thr, nxt_task_t *task,
nxt_runtime_t *runtime);
void nxt_main_stop_all_processes(nxt_task_t *task, nxt_runtime_t *runtime);
diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c
index ca0e698c..91ba3cb0 100644
--- a/src/nxt_openssl.c
+++ b/src/nxt_openssl.c
@@ -40,6 +40,7 @@ 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_uint_t nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd);
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);
@@ -246,7 +247,8 @@ static nxt_int_t
nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
{
SSL_CTX *ctx;
- const char *certificate, *key, *ciphers, *ca_certificate;
+ nxt_fd_t fd;
+ const char *ciphers, *ca_certificate;
STACK_OF(X509_NAME) *list;
ctx = SSL_CTX_new(SSLv23_server_method());
@@ -284,15 +286,14 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
#endif
- certificate = conf->certificate;
+ fd = conf->chain_file;
- if (SSL_CTX_use_certificate_chain_file(ctx, certificate) == 0) {
+ if (nxt_openssl_chain_file(ctx, fd) != NXT_OK) {
nxt_openssl_log_error(task, NXT_LOG_ALERT,
- "SSL_CTX_use_certificate_file(\"%s\") failed",
- certificate);
+ "nxt_openssl_chain_file() failed");
goto fail;
}
-
+/*
key = conf->certificate_key;
if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) == 0) {
@@ -301,7 +302,7 @@ nxt_openssl_server_init(nxt_task_t *task, nxt_tls_conf_t *conf)
key);
goto fail;
}
-
+*/
ciphers = (conf->ciphers != NULL) ? conf->ciphers : "HIGH:!aNULL:!MD5";
if (SSL_CTX_set_cipher_list(ctx, ciphers) == 0) {
@@ -358,6 +359,85 @@ fail:
}
+static nxt_uint_t
+nxt_openssl_chain_file(SSL_CTX *ctx, nxt_fd_t fd)
+{
+ BIO *bio;
+ X509 *cert, *ca;
+ long reason;
+ EVP_PKEY *key;
+ nxt_uint_t ret;
+
+ bio = BIO_new(BIO_s_fd());
+ if (bio == NULL) {
+ return NXT_ERROR;
+ }
+
+ BIO_set_fd(bio, fd, BIO_CLOSE);
+
+ ret = NXT_ERROR;
+
+ cert = PEM_read_bio_X509_AUX(bio, NULL, NULL, NULL);
+ if (cert == NULL) {
+ goto end;
+ }
+
+ if (SSL_CTX_use_certificate(ctx, cert) != 1) {
+ goto end;
+ }
+
+ for ( ;; ) {
+ ca = PEM_read_bio_X509(bio, NULL, NULL, NULL);
+
+ if (ca == NULL) {
+ reason = ERR_GET_REASON(ERR_peek_last_error());
+ if (reason != PEM_R_NO_START_LINE) {
+ goto end;
+ }
+
+ ERR_clear_error();
+ break;
+ }
+
+ /*
+ * Note that ca isn't freed if it was successfully added to the chain,
+ * while the main certificate needs a X509_free() call, since
+ * its reference count is increased by SSL_CTX_use_certificate().
+ */
+#if OPENSSL_VERSION_NUMBER > 0x10002000L
+ if (SSL_CTX_add0_chain_cert(ctx, ca) != 1) {
+#else
+ if (SSL_CTX_add_extra_chain_cert(ctx, ca) != 1) {
+#endif
+ X509_free(ca);
+ goto end;
+ }
+ }
+
+ if (BIO_reset(bio) != 0) {
+ goto end;
+ }
+
+ key = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ if (key == NULL) {
+ goto end;
+ }
+
+ if (SSL_CTX_use_PrivateKey(ctx, key) == 1) {
+ ret = NXT_OK;
+ }
+
+ EVP_PKEY_free(key);
+
+end:
+
+ X509_free(cert);
+ BIO_free(bio);
+
+ return ret;
+}
+
+
static void
nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf)
{
diff --git a/src/nxt_port.h b/src/nxt_port.h
index 1d070a89..76faa7d2 100644
--- a/src/nxt_port.h
+++ b/src/nxt_port.h
@@ -18,6 +18,8 @@ struct nxt_port_handlers_s {
nxt_port_handler_t socket;
nxt_port_handler_t modules;
nxt_port_handler_t conf_store;
+ nxt_port_handler_t cert_get;
+ nxt_port_handler_t cert_delete;
nxt_port_handler_t access_log;
/* File descriptor exchange. */
@@ -57,6 +59,8 @@ typedef enum {
_NXT_PORT_MSG_SOCKET = nxt_port_handler_idx(socket),
_NXT_PORT_MSG_MODULES = nxt_port_handler_idx(modules),
_NXT_PORT_MSG_CONF_STORE = nxt_port_handler_idx(conf_store),
+ _NXT_PORT_MSG_CERT_GET = nxt_port_handler_idx(cert_get),
+ _NXT_PORT_MSG_CERT_DELETE = nxt_port_handler_idx(cert_delete),
_NXT_PORT_MSG_ACCESS_LOG = nxt_port_handler_idx(access_log),
_NXT_PORT_MSG_CHANGE_FILE = nxt_port_handler_idx(change_file),
@@ -81,6 +85,8 @@ typedef enum {
NXT_PORT_MSG_SOCKET = _NXT_PORT_MSG_SOCKET | NXT_PORT_MSG_LAST,
NXT_PORT_MSG_MODULES = _NXT_PORT_MSG_MODULES | NXT_PORT_MSG_LAST,
NXT_PORT_MSG_CONF_STORE = _NXT_PORT_MSG_CONF_STORE | NXT_PORT_MSG_LAST,
+ NXT_PORT_MSG_CERT_GET = _NXT_PORT_MSG_CERT_GET | NXT_PORT_MSG_LAST,
+ NXT_PORT_MSG_CERT_DELETE = _NXT_PORT_MSG_CERT_DELETE | NXT_PORT_MSG_LAST,
NXT_PORT_MSG_ACCESS_LOG = _NXT_PORT_MSG_ACCESS_LOG | NXT_PORT_MSG_LAST,
NXT_PORT_MSG_CHANGE_FILE = _NXT_PORT_MSG_CHANGE_FILE | NXT_PORT_MSG_LAST,
diff --git a/src/nxt_router.c b/src/nxt_router.c
index 87721ba4..21a7b4d7 100644
--- a/src/nxt_router.c
+++ b/src/nxt_router.c
@@ -7,6 +7,9 @@
#include <nxt_router.h>
#include <nxt_conf.h>
+#if (NXT_TLS)
+#include <nxt_cert.h>
+#endif
#include <nxt_http.h>
#include <nxt_port_memory_int.h>
#include <nxt_unit_request.h>
@@ -32,6 +35,18 @@ typedef struct {
} nxt_router_listener_conf_t;
+#if (NXT_TLS)
+
+typedef struct {
+ nxt_str_t name;
+ nxt_socket_conf_t *conf;
+
+ nxt_queue_link_t link; /* for nxt_socket_conf_t.tls */
+} nxt_router_tlssock_t;
+
+#endif
+
+
typedef struct nxt_msg_info_s {
nxt_buf_t *buf;
nxt_port_mmap_tracking_t tracking;
@@ -156,6 +171,12 @@ static void nxt_router_listen_socket_ready(nxt_task_t *task,
nxt_port_recv_msg_t *msg, void *data);
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);
+static void nxt_router_tls_rpc_handler(nxt_task_t *task,
+ nxt_port_recv_msg_t *msg, void *data);
+#endif
static void nxt_router_app_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_app_t *app);
static void nxt_router_app_prefork_ready(nxt_task_t *task,
@@ -957,6 +978,10 @@ nxt_router_temp_conf(nxt_task_t *task)
nxt_queue_init(&tmcf->pending);
nxt_queue_init(&tmcf->creating);
+#if (NXT_TLS)
+ nxt_queue_init(&tmcf->tls);
+#endif
+
nxt_queue_init(&tmcf->apps);
nxt_queue_init(&tmcf->previous);
@@ -1002,6 +1027,9 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data)
nxt_router_conf_t *rtcf;
nxt_router_temp_conf_t *tmcf;
const nxt_event_interface_t *interface;
+#if (NXT_TLS)
+ nxt_router_tlssock_t *tls;
+#endif
tmcf = obj;
@@ -1018,6 +1046,19 @@ nxt_router_conf_apply(nxt_task_t *task, void *obj, void *data)
return;
}
+#if (NXT_TLS)
+ qlk = nxt_queue_first(&tmcf->tls);
+
+ if (qlk != nxt_queue_tail(&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);
+ return;
+ }
+#endif
+
nxt_queue_each(app, &tmcf->apps, nxt_app_t, link) {
if (nxt_router_app_need_start(app)) {
@@ -1332,11 +1373,17 @@ 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");
static nxt_str_t listeners_path = nxt_string("/listeners");
static nxt_str_t access_log_path = nxt_string("/access_log");
+#if (NXT_TLS)
+ static nxt_str_t certificate_path = nxt_string("/tls/certificate");
+#endif
conf = nxt_conf_json_parse(tmcf->mem_pool, start, end, NULL);
if (conf == NULL) {
@@ -1588,6 +1635,26 @@ 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);
+
+ if (value != NULL) {
+ nxt_conf_get_string(value, &name);
+
+ tls = nxt_mp_get(mp, sizeof(nxt_router_tlssock_t));
+ if (nxt_slow_path(tls == NULL)) {
+ goto fail;
+ }
+
+ tls->name = name;
+ tls->conf = skcf;
+
+ nxt_queue_insert_tail(&tmcf->tls, &tls->link);
+ }
+
+#endif
+
skcf->listen->handler = nxt_http_conn_init;
skcf->router_conf = tmcf->router_conf;
skcf->router_conf->count++;
@@ -1956,6 +2023,75 @@ nxt_router_listen_socket_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
}
+#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_socket_rpc_t *rpc;
+
+ rpc = nxt_mp_alloc(tmcf->mem_pool, sizeof(nxt_socket_rpc_t));
+ if (rpc == NULL) {
+ nxt_router_conf_error(task, tmcf);
+ return;
+ }
+
+ rpc->socket_conf = tls->conf;
+ rpc->temp_conf = tmcf;
+
+ nxt_cert_store_get(task, &tls->name, tmcf->mem_pool,
+ nxt_router_tls_rpc_handler, rpc);
+}
+
+
+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_debug(task, "tls rpc handler");
+
+ rpc = data;
+ tmcf = rpc->temp_conf;
+
+ if (msg == NULL || msg->port_msg.type == _NXT_PORT_MSG_RPC_ERROR) {
+ goto fail;
+ }
+
+ mp = tmcf->router_conf->mem_pool;
+
+ tlscf = nxt_mp_zget(mp, sizeof(nxt_tls_conf_t));
+ if (nxt_slow_path(tlscf == NULL)) {
+ goto fail;
+ }
+
+ tlscf->chain_file = msg->fd;
+
+ ret = task->thread->runtime->tls->server_init(task, tlscf);
+ 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;
+
+fail:
+
+ nxt_router_conf_error(task, tmcf);
+}
+
+#endif
+
+
static void
nxt_router_app_rpc_create(nxt_task_t *task,
nxt_router_temp_conf_t *tmcf, nxt_app_t *app)
diff --git a/src/nxt_router.h b/src/nxt_router.h
index a75637ac..a2da8ff9 100644
--- a/src/nxt_router.h
+++ b/src/nxt_router.h
@@ -59,6 +59,10 @@ typedef struct {
nxt_queue_t keeping; /* of nxt_socket_conf_t */
nxt_queue_t deleting; /* of nxt_socket_conf_t */
+#if (NXT_TLS)
+ nxt_queue_t tls; /* of nxt_router_tlssock_t */
+#endif
+
nxt_queue_t apps; /* of nxt_app_t */
nxt_queue_t previous; /* of nxt_app_t */
diff --git a/src/nxt_runtime.c b/src/nxt_runtime.c
index acc2820a..311c2c08 100644
--- a/src/nxt_runtime.c
+++ b/src/nxt_runtime.c
@@ -762,6 +762,23 @@ nxt_runtime_conf_init(nxt_task_t *task, nxt_runtime_t *rt)
rt->conf_tmp = (char *) file_name.start;
+ ret = nxt_file_name_create(rt->mem_pool, &file_name, "%s%scerts/%Z",
+ rt->state, slash);
+ if (nxt_slow_path(ret != NXT_OK)) {
+ return NXT_ERROR;
+ }
+
+ ret = mkdir((char *) file_name.start, S_IRWXU);
+
+ if (nxt_fast_path(ret == 0 || nxt_errno == EEXIST)) {
+ rt->certs.length = file_name.len;
+ rt->certs.start = file_name.start;
+
+ } else {
+ nxt_alert(task, "Unable to create certificates storage directory: "
+ "mkdir(%s) failed %E", file_name.start, nxt_errno);
+ }
+
control.length = nxt_strlen(rt->control);
control.start = (u_char *) rt->control;
diff --git a/src/nxt_runtime.h b/src/nxt_runtime.h
index 2e121f88..e4c9e2a1 100644
--- a/src/nxt_runtime.h
+++ b/src/nxt_runtime.h
@@ -68,6 +68,8 @@ struct nxt_runtime_s {
const char *conf_tmp;
const char *control;
+ nxt_str_t certs;
+
nxt_queue_t engines; /* of nxt_event_engine_t */
nxt_sockaddr_t *controller_listen;
diff --git a/src/nxt_tls.h b/src/nxt_tls.h
index 6f342edd..d9fcc6a8 100644
--- a/src/nxt_tls.h
+++ b/src/nxt_tls.h
@@ -44,8 +44,7 @@ struct nxt_tls_conf_s {
const nxt_tls_lib_t *lib;
- char *certificate;
- char *certificate_key;
+ nxt_fd_t chain_file;
char *ciphers;
char *ca_certificate;