From fddde539c9cde818857f46c24eaa7d4e57eb9b44 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 2 Mar 2021 18:30:34 +0300 Subject: Fixing NetBSD compatibility. Instead of PTHREAD_STACK_MIN define, NetBSD requires to get minimum stack size using sysctl(_SC_THREAD_STACK_MIN). This change originally proposed by Juraj Lutter . --- src/nxt_conf_validation.c | 4 ++-- src/nxt_thread.h | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_conf_validation.c b/src/nxt_conf_validation.c index 67fa3095..0e6fc135 100644 --- a/src/nxt_conf_validation.c +++ b/src/nxt_conf_validation.c @@ -1459,10 +1459,10 @@ nxt_conf_vldt_thread_stack_size(nxt_conf_validation_t *vldt, size = nxt_conf_get_number(value); - if (size < PTHREAD_STACK_MIN) { + if (size < NXT_THREAD_STACK_MIN) { return nxt_conf_vldt_error(vldt, "The \"thread_stack_size\" number " "must be equal to or greater than %d.", - PTHREAD_STACK_MIN); + NXT_THREAD_STACK_MIN); } if ((size % nxt_pagesize) != 0) { diff --git a/src/nxt_thread.h b/src/nxt_thread.h index 2ebc331d..d7800cc6 100644 --- a/src/nxt_thread.h +++ b/src/nxt_thread.h @@ -142,6 +142,14 @@ nxt_thread_yield() \ #endif +#if (PTHREAD_STACK_MIN) +#define NXT_THREAD_STACK_MIN PTHREAD_STACK_MIN + +#else +#define NXT_THREAD_STACK_MIN sysconf(_SC_THREAD_STACK_MIN) +#endif + + struct nxt_thread_s { nxt_log_t *log; nxt_log_t main_log; -- cgit From 73ac0496feb38ef2098163847206d304cf2c9a73 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 2 Mar 2021 18:31:03 +0300 Subject: Fixing warnings on Solaris. pthread_t on Solaris is an integer type with size not equal to pointer size. To avoid warnings, type casts to and from pointer needs to be done via uintptr_t type. This change originally proposed by Juraj Lutter . --- src/nxt_router.c | 2 +- src/nxt_unit.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index 03fe2a6c..4be4197a 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -3726,7 +3726,7 @@ nxt_router_thread_exit_handler(nxt_task_t *task, void *obj, void *data) nxt_event_engine_t *engine; nxt_thread_handle_t handle; - handle = (nxt_thread_handle_t) obj; + handle = (nxt_thread_handle_t) (uintptr_t) obj; link = data; nxt_thread_wait(handle); diff --git a/src/nxt_unit.c b/src/nxt_unit.c index 2fef17c5..ae4499d8 100644 --- a/src/nxt_unit.c +++ b/src/nxt_unit.c @@ -3785,7 +3785,7 @@ nxt_unit_shm_open(nxt_unit_ctx_t *ctx, size_t size) char name[64]; snprintf(name, sizeof(name), NXT_SHM_PREFIX "unit.%d.%p", - lib->pid, (void *) pthread_self()); + lib->pid, (void *) (uintptr_t) pthread_self()); #endif #if (NXT_HAVE_MEMFD_CREATE) -- cgit From 1950d0aee2bc95f1cb6a07349f11eb5e5f6edf5c Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Tue, 2 Mar 2021 19:33:37 +0300 Subject: Closing app outgoing shared memory file descriptor. This fixes file descriptor leakage in router. Shared memory file used to send data from router to application. These files are shared among all processes of same application and router keeps the opened file descriptor since 06017e6e3a5f commit. --- src/nxt_port_memory.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src') diff --git a/src/nxt_port_memory.c b/src/nxt_port_memory.c index ae9f079c..bffae8a1 100644 --- a/src/nxt_port_memory.c +++ b/src/nxt_port_memory.c @@ -34,6 +34,10 @@ nxt_port_mmap_handler_use(nxt_port_mmap_handler_t *mmap_handler, int i) mmap_handler->hdr = NULL; } + if (mmap_handler->fd != -1) { + nxt_fd_close(mmap_handler->fd); + } + nxt_free(mmap_handler); } } @@ -238,6 +242,7 @@ nxt_port_incoming_port_mmap(nxt_task_t *task, nxt_process_t *process, } mmap_handler->hdr = hdr; + mmap_handler->fd = -1; if (nxt_slow_path(hdr->src_pid != process->pid || hdr->dst_pid != nxt_pid)) -- cgit From a1107e859b5f401261d378fc251667bcf2272536 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 15 Mar 2021 14:57:01 +0300 Subject: Fixed TLS connection shutdown on errors. An immediate return statement on connection errors was mistakenly added to the beginning of nxt_openssl_conn_io_shutdown() in ecd3c5bbf7d8, breaking the TLS connection finalization procedure. As a result, a TLS connection was left unfinalized if it had been closed prematurely or a fatal protocol error had occurred, which caused memory and socket descriptor leakage. Moreover, in some cases (notably, on handshake errors in tests with kqueue on macOS) the read event was triggered later and nxt_h1p_conn_error() was called the second time; after the change in af93c866b4f0, the latter call crashed the router process in an attempt to remove a connection from the idle queue twice. --- src/nxt_openssl.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src') diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 832d1f0d..3c0212f7 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -720,10 +720,6 @@ nxt_openssl_conn_io_shutdown(nxt_task_t *task, void *obj, void *data) nxt_debug(task, "openssl conn shutdown fd:%d", c->socket.fd); - if (c->socket.error != 0) { - return; - } - c->read_state = NULL; tls = c->u.tls; -- cgit From b0a1266835386590c65ec433759c5cc1063bd472 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 15 Mar 2021 14:57:31 +0300 Subject: Fixed certificates loading on startup with some filesystems. It appears that readdir() on Linux detects file types unreliably, always setting the "d_type" field to DT_UNKNOWN for some less common filesystems. As a result, all files were skipped and no certificate bundles were found when the state directory was located on such filesystems. Skipping "." and ".." instead of any non-regular files should be enough, as no other non-regular files normally appear in this directory. This closes #368 issue on GitHub. --- src/nxt_cert.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/nxt_cert.c b/src/nxt_cert.c index 9e825d80..357a9211 100644 --- a/src/nxt_cert.c +++ b/src/nxt_cert.c @@ -838,7 +838,12 @@ nxt_cert_store_load(nxt_task_t *task, nxt_mp_t *mp) break; } - if (de->d_type != DT_REG) { + nxt_debug(task, "readdir(\"%s\"): \"%s\"", rt->certs.start, de->d_name); + + name.length = nxt_strlen(de->d_name); + name.start = (u_char *) de->d_name; + + if (nxt_str_eq(&name, ".", 1) || nxt_str_eq(&name, "..", 2)) { continue; } @@ -849,9 +854,6 @@ nxt_cert_store_load(nxt_task_t *task, nxt_mp_t *mp) 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) { -- cgit From 4c261a7ff8f2b2af08cc2cdee6828af4a1cf7794 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 15 Mar 2021 15:03:32 +0300 Subject: Ruby: fixed encodings initialization. The Ruby interpreter expects an explicit setlocale() call before initialization to pick up character encodings in the "Encoding" class from the environment. This closes #531 issue on GitHub. --- src/ruby/nxt_ruby.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/ruby/nxt_ruby.c b/src/ruby/nxt_ruby.c index 0aad887d..ca14af5b 100644 --- a/src/ruby/nxt_ruby.c +++ b/src/ruby/nxt_ruby.c @@ -12,6 +12,8 @@ #include NXT_RUBY_MOUNTS_H +#include + #define NXT_RUBY_RACK_API_VERSION_MAJOR 1 #define NXT_RUBY_RACK_API_VERSION_MINOR 3 @@ -200,6 +202,8 @@ nxt_ruby_start(nxt_task_t *task, nxt_process_data_t *data) nxt_ruby_threads = c->threads; + setlocale(LC_CTYPE, ""); + RUBY_INIT_STACK ruby_init(); ruby_options(2, argv); -- cgit From 99337728edbc38ac979b0f23805dbe74920a6bc2 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Mon, 15 Mar 2021 15:03:32 +0300 Subject: Fixed building the PHP 5 module with ZTS, broken by dab8544b5440. This closes #525 issue on GitHub. --- src/nxt_php_sapi.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'src') diff --git a/src/nxt_php_sapi.c b/src/nxt_php_sapi.c index 369e7f32..8fbe7f65 100644 --- a/src/nxt_php_sapi.c +++ b/src/nxt_php_sapi.c @@ -1041,7 +1041,11 @@ nxt_php_execute(nxt_php_run_ctx_t *ctx, nxt_unit_request_t *r) if (r->authorization_field != NXT_UNIT_NONE_FIELD) { f = r->fields + r->authorization_field; +#ifdef NXT_PHP7 php_handle_auth_data(nxt_unit_sptr_get(&f->value)); +#else + php_handle_auth_data(nxt_unit_sptr_get(&f->value) TSRMLS_CC); +#endif } else { SG(request_info).auth_digest = NULL; -- cgit From b04832da844d1c9e4ce7f7ff387059fbd07f78d3 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 24 Mar 2021 08:05:07 +0300 Subject: Disabled logging alerts to syslog. It feels to be causing more harm than good, because syslog() can be blocking, which is even more critical under resource exhaustion conditions when some alerts are expected. --- src/nxt_app_log.c | 9 ++++++++- src/nxt_log.c | 9 ++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_app_log.c b/src/nxt_app_log.c index 0ecd9aac..ae57c2a2 100644 --- a/src/nxt_app_log.c +++ b/src/nxt_app_log.c @@ -19,7 +19,10 @@ static u_char *nxt_log_debug_time(u_char *buf, nxt_realtime_t *now, void nxt_cdecl nxt_log_time_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) { - u_char *p, *syslogmsg, *end; + u_char *p, *end; +#if 0 + u_char *syslogmsg; +#endif va_list args; nxt_thread_t *thr; nxt_time_string_t *time_cache; @@ -34,7 +37,9 @@ nxt_log_time_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) p = nxt_thread_time_string(thr, time_cache, msg); +#if 0 syslogmsg = p; +#endif #if 0 nxt_fid_t fid; @@ -79,6 +84,7 @@ nxt_log_time_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) (void) nxt_write_console(nxt_stderr, msg, p - msg); +#if 0 if (level == NXT_LOG_ALERT) { *(p - nxt_length("\n")) = '\0'; @@ -88,6 +94,7 @@ nxt_log_time_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) */ nxt_write_syslog(LOG_ALERT, syslogmsg); } +#endif } diff --git a/src/nxt_log.c b/src/nxt_log.c index 1c666961..58756816 100644 --- a/src/nxt_log.c +++ b/src/nxt_log.c @@ -66,7 +66,10 @@ nxt_log_set_ctx(nxt_log_t *log, nxt_log_ctx_handler_t handler, void *ctx) void nxt_cdecl nxt_log_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) { - u_char *p, *syslogmsg, *end; + u_char *p, *end; +#if 0 + u_char *syslogmsg; +#endif va_list args; u_char msg[NXT_MAX_ERROR_STR]; @@ -79,7 +82,9 @@ nxt_log_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) *p++ = ' '; } +#if 0 syslogmsg = p; +#endif p = nxt_sprintf(p, end, (log->ident != 0) ? "[%V] *%D " : "[%V] ", &nxt_log_levels[level], log->ident); @@ -100,6 +105,7 @@ nxt_log_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) (void) nxt_write_console(nxt_stderr, msg, p - msg); +#if 0 if (level == NXT_LOG_ALERT) { *(p - nxt_length("\n")) = '\0'; @@ -109,4 +115,5 @@ nxt_log_handler(nxt_uint_t level, nxt_log_t *log, const char *fmt, ...) */ nxt_write_syslog(LOG_ALERT, syslogmsg); } +#endif } -- cgit From f267dd0a8da280d2a803b61c9a309fe51d60d95a Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Wed, 24 Mar 2021 11:43:31 +0300 Subject: Workaround for an OpenSSL bug about not closing /dev/*random. This is a workaround for an issue in OpenSSL 1.1.1, where the /dev/random and /dev/urandom files remain open after all listening sockets were removed: - https://github.com/openssl/openssl/issues/7419 --- src/nxt_openssl.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'src') diff --git a/src/nxt_openssl.c b/src/nxt_openssl.c index 3c0212f7..835ca8b2 100644 --- a/src/nxt_openssl.c +++ b/src/nxt_openssl.c @@ -8,6 +8,7 @@ #include #include #include +#include typedef struct { @@ -355,6 +356,11 @@ fail: SSL_CTX_free(ctx); +#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \ + && OPENSSL_VERSION_NUMBER < 0x1010101fL) + RAND_keep_random_devices_open(0); +#endif + return NXT_ERROR; } @@ -442,6 +448,11 @@ static void nxt_openssl_server_free(nxt_task_t *task, nxt_tls_conf_t *conf) { SSL_CTX_free(conf->ctx); + +#if (OPENSSL_VERSION_NUMBER >= 0x1010100fL \ + && OPENSSL_VERSION_NUMBER < 0x1010101fL) + RAND_keep_random_devices_open(0); +#endif } -- cgit From f18a41c84bb573607eaab9fec0c070cd159493f0 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 24 Mar 2021 16:38:05 +0300 Subject: Certficates: fixed counting DNS SAN entries. Previously, entries of any type were counted during object allocation but only DNS type entries were actually processed. As a result, if some certificate entries had another type, returning information about the certificate caused uninitialized memory access. --- src/nxt_cert.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/nxt_cert.c b/src/nxt_cert.c index 357a9211..0b986b0d 100644 --- a/src/nxt_cert.c +++ b/src/nxt_cert.c @@ -722,13 +722,16 @@ nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer) if (alt_names != NULL) { count = sk_GENERAL_NAME_num(alt_names); + n = 0; - for (n = 0; n != count; n++) { - name = sk_GENERAL_NAME_value(alt_names, n); + for (i = 0; i != count; i++) { + name = sk_GENERAL_NAME_value(alt_names, i); if (name->type != GEN_DNS) { continue; } + + n++; } names = nxt_conf_create_array(mp, n); -- cgit From a6c6dcf5f7856a96881373a2dbd1f14bda396c45 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 24 Mar 2021 16:38:05 +0300 Subject: Certificates: moved SAN processing to a separate function. No functional changes. --- src/nxt_cert.c | 92 +++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 36 deletions(-) (limited to 'src') diff --git a/src/nxt_cert.c b/src/nxt_cert.c index 0b986b0d..f3f4bace 100644 --- a/src/nxt_cert.c +++ b/src/nxt_cert.c @@ -46,6 +46,8 @@ 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_conf_value_t *nxt_cert_alt_names_details(nxt_mp_t *mp, + STACK_OF(GENERAL_NAME) *alt_names); static nxt_lvlhsh_t nxt_cert_info; @@ -654,7 +656,6 @@ nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer) 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]; @@ -721,46 +722,14 @@ nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer) } if (alt_names != NULL) { - count = sk_GENERAL_NAME_num(alt_names); - n = 0; + names = nxt_cert_alt_names_details(mp, alt_names); - for (i = 0; i != count; i++) { - name = sk_GENERAL_NAME_value(alt_names, i); - - if (name->type != GEN_DNS) { - continue; - } - - n++; - } + sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); - 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; - } + return NULL; } - sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); - nxt_conf_set_member(object, &alt_names_str, names, 1); } @@ -776,6 +745,57 @@ fail: } +static nxt_conf_value_t * +nxt_cert_alt_names_details(nxt_mp_t *mp, STACK_OF(GENERAL_NAME) *alt_names) +{ + nxt_str_t str; + nxt_int_t ret; + nxt_uint_t i, n, count; + GENERAL_NAME *name; + nxt_conf_value_t *array; + + count = sk_GENERAL_NAME_num(alt_names); + n = 0; + + for (i = 0; i != count; i++) { + name = sk_GENERAL_NAME_value(alt_names, i); + + if (name->type != GEN_DNS) { + continue; + } + + n++; + } + + array = nxt_conf_create_array(mp, n); + if (nxt_slow_path(array == NULL)) { + return NULL; + } + + 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(array, mp, i++, &str); + if (nxt_slow_path(ret != NXT_OK)) { + return NULL; + } + } + + return array; +} + + nxt_int_t nxt_cert_info_delete(nxt_str_t *name) { -- cgit From 699a3ea2ebc86f9e9dc9d59e1d9db488ac4ff352 Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Wed, 24 Mar 2021 16:55:47 +0300 Subject: Certificates: fixed in name attributes processing. The idea is to put SAN after CN, but the previous version of the code incorrectly assumed that CN was always present, which caused writes outside the allocated object if there were no standard name attributes. --- src/nxt_cert.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) (limited to 'src') diff --git a/src/nxt_cert.c b/src/nxt_cert.c index f3f4bace..3cdb69c1 100644 --- a/src/nxt_cert.c +++ b/src/nxt_cert.c @@ -690,12 +690,23 @@ nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer) NULL, NULL); if (alt_names != NULL) { + names = nxt_cert_alt_names_details(mp, alt_names); + + sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); + + if (nxt_slow_path(names == NULL)) { + return NULL; + } + count++; + + } else { + names = NULL; } object = nxt_conf_create_object(mp, count); if (nxt_slow_path(object == NULL)) { - goto fail; + return NULL; } for (n = 0, i = 0; n != nxt_nitems(nids) && i != count; n++) { @@ -703,12 +714,12 @@ nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer) len = X509_NAME_get_text_by_NID(x509_name, nids[n].nid, (char *) buf, sizeof(buf)); - if (len < 0) { - continue; + if (n == 1 && names != NULL) { + nxt_conf_set_member(object, &alt_names_str, names, i++); } - if (i == 1 && alt_names != NULL) { - i++; + if (len < 0) { + continue; } str.length = len; @@ -717,31 +728,11 @@ nxt_cert_name_details(nxt_mp_t *mp, X509 *x509, nxt_bool_t issuer) 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) { - names = nxt_cert_alt_names_details(mp, alt_names); - - sk_GENERAL_NAME_pop_free(alt_names, GENERAL_NAME_free); - - if (nxt_slow_path(names == NULL)) { return NULL; } - - 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; } -- cgit From d2b0882d89f29fea84b457e0709b6980c8a30a57 Mon Sep 17 00:00:00 2001 From: Andrey Suvorov Date: Wed, 24 Mar 2021 13:19:36 -0700 Subject: 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. --- src/nxt_conf_validation.c | 29 +++- src/nxt_openssl.c | 404 ++++++++++++++++++++++++++++++++++++++++++---- src/nxt_router.c | 123 ++++++++++---- src/nxt_tls.h | 29 +++- 4 files changed, 517 insertions(+), 68 deletions(-) (limited to 'src') 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, }, @@ -1826,10 +1828,35 @@ nxt_conf_vldt_match_patterns_set_member(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) +{ + 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 #include #include +#include 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; -- cgit From 9957a959dfd6e60e6fce02c904ad6f768f69c44b Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 25 Mar 2021 14:16:30 +0300 Subject: Releasing shm buffers for large body requests. This fixes memory and shm file descriptor leakage that occurred when a large request body was passed via shared memory. The leakage was caught with the "test_settings_body_buffer_size" test. The main condition is the "body_buffer_size" value exceeding 10 Mb (a shm segment). Thus, the router was forced to split the body into several shm segments, but these buffers were not freed because of dummy completion handlers. --- src/nxt_router.c | 15 ++++----------- src/nxt_router_request.h | 1 - 2 files changed, 4 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/nxt_router.c b/src/nxt_router.c index 0ecaefa1..8524b358 100644 --- a/src/nxt_router.c +++ b/src/nxt_router.c @@ -523,12 +523,11 @@ nxt_router_msg_cancel(nxt_task_t *task, nxt_request_rpc_data_t *req_rpc_data) next = b->next; b->next = NULL; - b->completion_handler = msg_info->completion_handler; - if (b->is_port_mmap_sent) { b->is_port_mmap_sent = cancelled == 0; - b->completion_handler(task, b, b->parent); } + + b->completion_handler(task, b, b->parent); } msg_info->buf = NULL; @@ -4113,6 +4112,8 @@ nxt_router_req_headers_ack_handler(nxt_task_t *task, if (b != NULL) { /* First buffer is already sent. Start from second. */ b = b->next; + + req_rpc_data->msg_info.buf->next = NULL; } if (req_rpc_data->msg_info.body_fd != -1 || b != NULL) { @@ -5025,14 +5026,6 @@ nxt_router_app_prepare_request(nxt_task_t *task, port->socket.fd); req_rpc_data->msg_info.buf = buf; - req_rpc_data->msg_info.completion_handler = buf->completion_handler; - - do { - buf->completion_handler = nxt_router_dummy_buf_completion; - buf = buf->next; - } while (buf != NULL); - - buf = req_rpc_data->msg_info.buf; body = req_rpc_data->request->body; diff --git a/src/nxt_router_request.h b/src/nxt_router_request.h index 95044dbb..6e226bae 100644 --- a/src/nxt_router_request.h +++ b/src/nxt_router_request.h @@ -11,7 +11,6 @@ typedef struct { nxt_buf_t *buf; nxt_fd_t body_fd; uint32_t tracking_cookie; - nxt_work_handler_t completion_handler; } nxt_msg_info_t; -- cgit From 067c6096e2ec306c4fdae6993140fbbdf4f9a6fd Mon Sep 17 00:00:00 2001 From: Valentin Bartenev Date: Thu, 25 Mar 2021 16:15:03 +0300 Subject: Node.js: used distinct placeholder for version in "package.json". This makes the "sed" instruction simpler and more portable, as the previous variant didn't work well on BSD systems due to the "\s" metacharacter. Thanks to Sergey A. Osokin for spotting this issue. Also, this should prevent accidentally creating a version 1.0.0 package. --- src/nodejs/unit-http/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/nodejs/unit-http/package.json b/src/nodejs/unit-http/package.json index 7ee01346..81f06bd4 100644 --- a/src/nodejs/unit-http/package.json +++ b/src/nodejs/unit-http/package.json @@ -1,6 +1,6 @@ { "name": "unit-http", - "version": "1.0.0", + "version": "%%VERSION%%", "description": "HTTP module for NGINX Unit", "main": "http.js", "files": [ -- cgit From b8052b050e0111400c59f35e76c013d8ee553ea9 Mon Sep 17 00:00:00 2001 From: Max Romanov Date: Thu, 25 Mar 2021 16:55:16 +0300 Subject: Fixing shm buffer leakage when sending over the port queue. When the shm buffer is sent over the port queue, it needs to be completed because it's sent over the port socket. --- src/nxt_port_socket.c | 97 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 13 deletions(-) (limited to 'src') diff --git a/src/nxt_port_socket.c b/src/nxt_port_socket.c index 9d8096b2..3cf2e79a 100644 --- a/src/nxt_port_socket.c +++ b/src/nxt_port_socket.c @@ -6,8 +6,16 @@ #include #include +#include +#define NXT_PORT_MAX_ENQUEUE_BUF_SIZE \ + (int) (NXT_PORT_QUEUE_MSG_SIZE - sizeof(nxt_port_msg_t)) + + +static nxt_bool_t nxt_port_can_enqueue_buf(nxt_buf_t *b); +static uint8_t nxt_port_enqueue_buf(nxt_task_t *task, nxt_port_msg_t *pm, + void *qbuf, nxt_buf_t *b); static nxt_int_t nxt_port_msg_chk_insert(nxt_task_t *task, nxt_port_t *port, nxt_port_send_msg_t *msg); static nxt_port_send_msg_t *nxt_port_msg_alloc(nxt_port_send_msg_t *m); @@ -151,10 +159,13 @@ nxt_port_socket_write2(nxt_task_t *task, nxt_port_t *port, nxt_uint_t type, nxt_buf_t *b) { int notify; - uint8_t *p; + uint8_t qmsg_size; nxt_int_t res; nxt_port_send_msg_t msg; - uint8_t qmsg[NXT_PORT_QUEUE_MSG_SIZE]; + struct { + nxt_port_msg_t pm; + uint8_t buf[NXT_PORT_MAX_ENQUEUE_BUF_SIZE]; + } qmsg; msg.link.next = NULL; msg.link.prev = NULL; @@ -177,21 +188,31 @@ nxt_port_socket_write2(nxt_task_t *task, nxt_port_t *port, nxt_uint_t type, if (port->queue != NULL && type != _NXT_PORT_MSG_READ_QUEUE) { - if (fd == -1 - && (b == NULL - || nxt_buf_mem_used_size(&b->mem) - <= (int) (NXT_PORT_QUEUE_MSG_SIZE - sizeof(nxt_port_msg_t)))) - { - p = nxt_cpymem(qmsg, &msg.port_msg, sizeof(nxt_port_msg_t)); + if (fd == -1 && nxt_port_can_enqueue_buf(b)) { + qmsg.pm = msg.port_msg; + + qmsg_size = sizeof(qmsg.pm); + if (b != NULL) { - p = nxt_cpymem(p, b->mem.pos, nxt_buf_mem_used_size(&b->mem)); + qmsg_size += nxt_port_enqueue_buf(task, &qmsg.pm, qmsg.buf, b); } - res = nxt_port_queue_send(port->queue, qmsg, p - qmsg, ¬ify); + res = nxt_port_queue_send(port->queue, &qmsg, qmsg_size, ¬ify); nxt_debug(task, "port{%d,%d} %d: enqueue %d notify %d, %d", (int) port->pid, (int) port->id, port->socket.fd, - (int) (p - qmsg), notify, res); + (int) qmsg_size, notify, res); + + if (b != NULL && nxt_fast_path(res == NXT_OK)) { + if (qmsg.pm.mmap) { + b->is_port_mmap_sent = 1; + } + + b->mem.pos = b->mem.free; + + nxt_work_queue_add(&task->thread->engine->fast_work_queue, + b->completion_handler, task, b, b->parent); + } if (notify == 0) { return res; @@ -201,9 +222,9 @@ nxt_port_socket_write2(nxt_task_t *task, nxt_port_t *port, nxt_uint_t type, msg.buf = NULL; } else { - qmsg[0] = _NXT_PORT_MSG_READ_SOCKET; + qmsg.buf[0] = _NXT_PORT_MSG_READ_SOCKET; - res = nxt_port_queue_send(port->queue, qmsg, 1, ¬ify); + res = nxt_port_queue_send(port->queue, qmsg.buf, 1, ¬ify); nxt_debug(task, "port{%d,%d} %d: enqueue 1 notify %d, %d", (int) port->pid, (int) port->id, port->socket.fd, @@ -225,6 +246,56 @@ nxt_port_socket_write2(nxt_task_t *task, nxt_port_t *port, nxt_uint_t type, } +static nxt_bool_t +nxt_port_can_enqueue_buf(nxt_buf_t *b) +{ + if (b == NULL) { + return 1; + } + + if (b->next != NULL) { + return 0; + } + + return (nxt_buf_mem_used_size(&b->mem) <= NXT_PORT_MAX_ENQUEUE_BUF_SIZE + || nxt_buf_is_port_mmap(b)); +} + + +static uint8_t +nxt_port_enqueue_buf(nxt_task_t *task, nxt_port_msg_t *pm, void *qbuf, + nxt_buf_t *b) +{ + ssize_t size; + nxt_port_mmap_msg_t *mm; + nxt_port_mmap_header_t *hdr; + nxt_port_mmap_handler_t *mmap_handler; + + size = nxt_buf_mem_used_size(&b->mem); + + if (size <= NXT_PORT_MAX_ENQUEUE_BUF_SIZE) { + nxt_memcpy(qbuf, b->mem.pos, size); + + return size; + } + + mmap_handler = b->parent; + hdr = mmap_handler->hdr; + mm = qbuf; + + mm->mmap_id = hdr->id; + mm->chunk_id = nxt_port_mmap_chunk_id(hdr, b->mem.pos); + mm->size = nxt_buf_mem_used_size(&b->mem); + + pm->mmap = 1; + + nxt_debug(task, "mmap_msg={%D, %D, %D}", mm->mmap_id, mm->chunk_id, + mm->size); + + return sizeof(nxt_port_mmap_msg_t); +} + + static nxt_int_t nxt_port_msg_chk_insert(nxt_task_t *task, nxt_port_t *port, nxt_port_send_msg_t *msg) -- cgit