From b064ab9e74fec194b97de6835d39b0650ed88c04 Mon Sep 17 00:00:00 2001 From: chronolaw Date: Wed, 25 Oct 2023 10:00:31 +0800 Subject: [PATCH] nginx 1.25.3 --- nginx/CHANGES | 20 + nginx/CHANGES.ru | 22 + nginx/src/core/nginx.h | 4 +- nginx/src/core/ngx_module.h | 5 + nginx/src/core/ngx_queue.c | 52 +- nginx/src/core/ngx_queue.h | 3 + nginx/src/core/ngx_regex.c | 7 +- nginx/src/event/quic/ngx_event_quic.c | 57 +- nginx/src/event/quic/ngx_event_quic.h | 3 +- nginx/src/event/quic/ngx_event_quic_ack.c | 4 + .../event/quic/ngx_event_quic_connection.h | 2 + .../src/event/quic/ngx_event_quic_migration.c | 4 +- .../quic/ngx_event_quic_openssl_compat.c | 118 ++-- .../quic/ngx_event_quic_openssl_compat.h | 2 - nginx/src/event/quic/ngx_event_quic_output.c | 19 + .../event/quic/ngx_event_quic_protection.c | 578 +++++++++++------- .../event/quic/ngx_event_quic_protection.h | 21 +- nginx/src/event/quic/ngx_event_quic_socket.c | 4 +- nginx/src/event/quic/ngx_event_quic_ssl.c | 30 +- nginx/src/event/quic/ngx_event_quic_streams.c | 4 + .../src/event/quic/ngx_event_quic_transport.c | 2 +- .../http/modules/ngx_http_fastcgi_module.c | 5 +- nginx/src/http/modules/ngx_http_scgi_module.c | 5 +- .../src/http/modules/ngx_http_uwsgi_module.c | 5 +- nginx/src/http/ngx_http_request.h | 4 - nginx/src/http/v2/ngx_http_v2.c | 23 +- nginx/src/http/v2/ngx_http_v2.h | 2 + nginx/src/http/v2/ngx_http_v2_module.c | 2 +- nginx/src/http/v3/ngx_http_v3.c | 4 +- nginx/src/http/v3/ngx_http_v3.h | 9 +- nginx/src/http/v3/ngx_http_v3_module.c | 8 +- nginx/src/http/v3/ngx_http_v3_request.c | 18 +- 32 files changed, 652 insertions(+), 394 deletions(-) diff --git a/nginx/CHANGES b/nginx/CHANGES index 74544a1f..d8c6c959 100644 --- a/nginx/CHANGES +++ b/nginx/CHANGES @@ -1,4 +1,24 @@ +Changes with nginx 1.25.3 24 Oct 2023 + + *) Change: improved detection of misbehaving clients when using HTTP/2. + + *) Feature: startup speedup when using a large number of locations. + Thanks to Yusuke Nojima. + + *) Bugfix: a segmentation fault might occur in a worker process when + using HTTP/2 without SSL; the bug had appeared in 1.25.1. + + *) Bugfix: the "Status" backend response header line with an empty + reason phrase was handled incorrectly. + + *) Bugfix: memory leak during reconfiguration when using the PCRE2 + library. + Thanks to ZhenZhong Wu. + + *) Bugfixes and improvements in HTTP/3. + + Changes with nginx 1.25.2 15 Aug 2023 *) Feature: path MTU discovery when using HTTP/3. diff --git a/nginx/CHANGES.ru b/nginx/CHANGES.ru index 5b1fa530..9c06338c 100644 --- a/nginx/CHANGES.ru +++ b/nginx/CHANGES.ru @@ -1,4 +1,26 @@ +Изменения в nginx 1.25.3 24.10.2023 + + *) Изменение: улучшено детектирование некорректного поведения клиентов + при использовании HTTP/2. + + *) Добавление: уменьшение времени запуска при использовании большого + количества location'ов. + Спасибо Yusuke Nojima. + + *) Исправление: при использовании HTTP/2 без SSL в рабочем процессе мог + произойти segmentation fault; ошибка появилась в 1.25.1. + + *) Исправление: строка "Status" в заголовке ответа бэкенда с пустой + поясняющей фразой обрабатывалась некорректно. + + *) Исправление: утечки памяти во время переконфигурации при + использовании библиотеки PCRE2. + Спасибо ZhenZhong Wu. + + *) Исправления и улучшения в HTTP/3. + + Изменения в nginx 1.25.2 15.08.2023 *) Добавление: path MTU discovery при использовании HTTP/3. diff --git a/nginx/src/core/nginx.h b/nginx/src/core/nginx.h index dba95c4a..088aa1ec 100644 --- a/nginx/src/core/nginx.h +++ b/nginx/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1025002 -#define NGINX_VERSION "1.25.2" +#define nginx_version 1025003 +#define NGINX_VERSION "1.25.3" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD diff --git a/nginx/src/core/ngx_module.h b/nginx/src/core/ngx_module.h index 6fb45542..a415cd6d 100644 --- a/nginx/src/core/ngx_module.h +++ b/nginx/src/core/ngx_module.h @@ -107,7 +107,12 @@ #endif #define NGX_MODULE_SIGNATURE_17 "0" + +#if (NGX_QUIC || NGX_COMPAT) +#define NGX_MODULE_SIGNATURE_18 "1" +#else #define NGX_MODULE_SIGNATURE_18 "0" +#endif #if (NGX_HAVE_OPENAT) #define NGX_MODULE_SIGNATURE_19 "1" diff --git a/nginx/src/core/ngx_queue.c b/nginx/src/core/ngx_queue.c index 3cacaf3a..3d1d5898 100644 --- a/nginx/src/core/ngx_queue.c +++ b/nginx/src/core/ngx_queue.c @@ -9,6 +9,10 @@ #include +static void ngx_queue_merge(ngx_queue_t *queue, ngx_queue_t *tail, + ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)); + + /* * find the middle queue element if the queue has odd number of elements * or the first element of the queue's second part otherwise @@ -45,13 +49,13 @@ ngx_queue_middle(ngx_queue_t *queue) } -/* the stable insertion sort */ +/* the stable merge sort */ void ngx_queue_sort(ngx_queue_t *queue, ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)) { - ngx_queue_t *q, *prev, *next; + ngx_queue_t *q, tail; q = ngx_queue_head(queue); @@ -59,22 +63,44 @@ ngx_queue_sort(ngx_queue_t *queue, return; } - for (q = ngx_queue_next(q); q != ngx_queue_sentinel(queue); q = next) { + q = ngx_queue_middle(queue); + + ngx_queue_split(queue, q, &tail); + + ngx_queue_sort(queue, cmp); + ngx_queue_sort(&tail, cmp); + + ngx_queue_merge(queue, &tail, cmp); +} + - prev = ngx_queue_prev(q); - next = ngx_queue_next(q); +static void +ngx_queue_merge(ngx_queue_t *queue, ngx_queue_t *tail, + ngx_int_t (*cmp)(const ngx_queue_t *, const ngx_queue_t *)) +{ + ngx_queue_t *q1, *q2; - ngx_queue_remove(q); + q1 = ngx_queue_head(queue); + q2 = ngx_queue_head(tail); - do { - if (cmp(prev, q) <= 0) { - break; - } + for ( ;; ) { + if (q1 == ngx_queue_sentinel(queue)) { + ngx_queue_add(queue, tail); + break; + } - prev = ngx_queue_prev(prev); + if (q2 == ngx_queue_sentinel(tail)) { + break; + } + + if (cmp(q1, q2) <= 0) { + q1 = ngx_queue_next(q1); + continue; + } - } while (prev != ngx_queue_sentinel(queue)); + ngx_queue_remove(q2); + ngx_queue_insert_before(q1, q2); - ngx_queue_insert_after(prev, q); + q2 = ngx_queue_head(tail); } } diff --git a/nginx/src/core/ngx_queue.h b/nginx/src/core/ngx_queue.h index 038bf121..0f82f173 100644 --- a/nginx/src/core/ngx_queue.h +++ b/nginx/src/core/ngx_queue.h @@ -47,6 +47,9 @@ struct ngx_queue_s { (h)->prev = x +#define ngx_queue_insert_before ngx_queue_insert_tail + + #define ngx_queue_head(h) \ (h)->next diff --git a/nginx/src/core/ngx_regex.c b/nginx/src/core/ngx_regex.c index 91381f49..5b13c5db 100644 --- a/nginx/src/core/ngx_regex.c +++ b/nginx/src/core/ngx_regex.c @@ -600,6 +600,8 @@ ngx_regex_cleanup(void *data) * the new cycle, these will be re-allocated. */ + ngx_regex_malloc_init(NULL); + if (ngx_regex_compile_context) { pcre2_compile_context_free(ngx_regex_compile_context); ngx_regex_compile_context = NULL; @@ -611,6 +613,8 @@ ngx_regex_cleanup(void *data) ngx_regex_match_data_size = 0; } + ngx_regex_malloc_done(); + #endif } @@ -706,9 +710,6 @@ ngx_regex_module_init(ngx_cycle_t *cycle) ngx_regex_malloc_done(); ngx_regex_studies = NULL; -#if (NGX_PCRE2) - ngx_regex_compile_context = NULL; -#endif return NGX_OK; } diff --git a/nginx/src/event/quic/ngx_event_quic.c b/nginx/src/event/quic/ngx_event_quic.c index fb211cc9..b0cf056c 100644 --- a/nginx/src/event/quic/ngx_event_quic.c +++ b/nginx/src/event/quic/ngx_event_quic.c @@ -211,6 +211,8 @@ ngx_quic_run(ngx_connection_t *c, ngx_quic_conf_t *conf) qc = ngx_quic_get_connection(c); ngx_add_timer(c->read, qc->tp.max_idle_timeout); + ngx_add_timer(&qc->close, qc->conf->handshake_timeout); + ngx_quic_connstate_dbg(c); c->read->handler = ngx_quic_input_handler; @@ -283,6 +285,10 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, qc->path_validation.data = c; qc->path_validation.handler = ngx_quic_path_handler; + qc->key_update.log = c->log; + qc->key_update.data = c; + qc->key_update.handler = ngx_quic_keys_update; + qc->conf = conf; if (ngx_quic_init_transport_params(&qc->tp, conf) != NGX_OK) { @@ -329,6 +335,7 @@ ngx_quic_new_connection(ngx_connection_t *c, ngx_quic_conf_t *conf, qc->validated = pkt->validated; if (ngx_quic_open_sockets(c, qc, pkt) != NGX_OK) { + ngx_quic_keys_cleanup(qc->keys); return NULL; } @@ -414,7 +421,7 @@ ngx_quic_input_handler(ngx_event_t *rev) if (c->close) { c->close = 0; - if (!ngx_exiting) { + if (!ngx_exiting || !qc->streams.initialized) { qc->error = NGX_QUIC_ERR_NO_ERROR; qc->error_reason = "graceful shutdown"; ngx_quic_close_connection(c, NGX_ERROR); @@ -481,6 +488,10 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) ngx_quic_free_frames(c, &qc->send_ctx[i].sent); } + if (qc->close.timer_set) { + ngx_del_timer(&qc->close); + } + if (rc == NGX_DONE) { /* @@ -505,9 +516,6 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) * to terminate the connection immediately. */ - qc->error_level = c->ssl ? SSL_quic_read_level(c->ssl->connection) - : ssl_encryption_initial; - if (qc->error == (ngx_uint_t) -1) { qc->error = NGX_QUIC_ERR_INTERNAL_ERROR; qc->error_app = 0; @@ -520,17 +528,19 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) qc->error_app ? "app " : "", qc->error, qc->error_reason ? qc->error_reason : ""); - if (rc == NGX_OK) { - ctx = ngx_quic_get_send_ctx(qc, qc->error_level); - ngx_add_timer(&qc->close, 3 * ngx_quic_pto(c, ctx)); - } + for (i = 0; i < NGX_QUIC_SEND_CTX_LAST; i++) { + ctx = &qc->send_ctx[i]; - (void) ngx_quic_send_cc(c); + if (!ngx_quic_keys_available(qc->keys, ctx->level, 1)) { + continue; + } - if (qc->error_level == ssl_encryption_handshake) { - /* for clients that might not have handshake keys */ - qc->error_level = ssl_encryption_initial; + qc->error_level = ctx->level; (void) ngx_quic_send_cc(c); + + if (rc == NGX_OK) { + ngx_add_timer(&qc->close, 3 * ngx_quic_pto(c, ctx)); + } } } @@ -562,6 +572,10 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) ngx_delete_posted_event(&qc->push); } + if (qc->key_update.posted) { + ngx_delete_posted_event(&qc->key_update); + } + if (qc->close.timer_set) { return; } @@ -572,6 +586,8 @@ ngx_quic_close_connection(ngx_connection_t *c, ngx_int_t rc) ngx_quic_close_sockets(c); + ngx_quic_keys_cleanup(qc->keys); + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic close completed"); /* may be tested from SSL callback during SSL shutdown */ @@ -946,7 +962,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt) c->log->action = "decrypting packet"; - if (!ngx_quic_keys_available(qc->keys, pkt->level)) { + if (!ngx_quic_keys_available(qc->keys, pkt->level, 0)) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic no %s keys, ignoring packet", ngx_quic_level_name(pkt->level)); @@ -956,10 +972,7 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt) #if !defined (OPENSSL_IS_BORINGSSL) /* OpenSSL provides read keys for an application level before it's ready */ - if (pkt->level == ssl_encryption_application - && SSL_quic_read_level(c->ssl->connection) - < ssl_encryption_application) - { + if (pkt->level == ssl_encryption_application && !c->ssl->handshaked) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "quic no %s keys ready, ignoring packet", ngx_quic_level_name(pkt->level)); @@ -1055,7 +1068,9 @@ ngx_quic_handle_payload(ngx_connection_t *c, ngx_quic_header_t *pkt) return rc; } - return ngx_quic_keys_update(c, qc->keys); + ngx_post_event(&qc->key_update, &ngx_posted_events); + + return NGX_OK; } @@ -1070,7 +1085,9 @@ ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level) qc = ngx_quic_get_connection(c); - if (!ngx_quic_keys_available(qc->keys, level)) { + if (!ngx_quic_keys_available(qc->keys, level, 0) + && !ngx_quic_keys_available(qc->keys, level, 1)) + { return; } @@ -1100,7 +1117,7 @@ ngx_quic_discard_ctx(ngx_connection_t *c, enum ssl_encryption_level_t level) } if (level == ssl_encryption_initial) { - /* close temporary listener with odcid */ + /* close temporary listener with initial dcid */ qsock = ngx_quic_find_socket(c, NGX_QUIC_UNSET_PN); if (qsock) { ngx_quic_close_socket(c, qsock); diff --git a/nginx/src/event/quic/ngx_event_quic.h b/nginx/src/event/quic/ngx_event_quic.h index ca15200b..15201671 100644 --- a/nginx/src/event/quic/ngx_event_quic.h +++ b/nginx/src/event/quic/ngx_event_quic.h @@ -67,7 +67,8 @@ typedef struct { ngx_flag_t retry; ngx_flag_t gso_enabled; ngx_flag_t disable_active_migration; - ngx_msec_t timeout; + ngx_msec_t handshake_timeout; + ngx_msec_t idle_timeout; ngx_str_t host_key; size_t stream_buffer_size; ngx_uint_t max_concurrent_streams_bidi; diff --git a/nginx/src/event/quic/ngx_event_quic_ack.c b/nginx/src/event/quic/ngx_event_quic_ack.c index 23c9af34..a7fd85d2 100644 --- a/nginx/src/event/quic/ngx_event_quic_ack.c +++ b/nginx/src/event/quic/ngx_event_quic_ack.c @@ -907,6 +907,10 @@ ngx_quic_ack_packet(ngx_connection_t *c, ngx_quic_header_t *pkt) " nranges:%ui", pkt->pn, (int64_t) ctx->largest_range, ctx->first_range, ctx->nranges); + if (!ngx_quic_keys_available(qc->keys, ctx->level, 1)) { + return NGX_OK; + } + prev_pending = ctx->pending_ack; if (pkt->need_ack) { diff --git a/nginx/src/event/quic/ngx_event_quic_connection.h b/nginx/src/event/quic/ngx_event_quic_connection.h index 5836f94a..d84e3e63 100644 --- a/nginx/src/event/quic/ngx_event_quic_connection.h +++ b/nginx/src/event/quic/ngx_event_quic_connection.h @@ -230,6 +230,8 @@ struct ngx_quic_connection_s { ngx_event_t pto; ngx_event_t close; ngx_event_t path_validation; + ngx_event_t key_update; + ngx_msec_t last_cc; ngx_msec_t first_rtt; diff --git a/nginx/src/event/quic/ngx_event_quic_migration.c b/nginx/src/event/quic/ngx_event_quic_migration.c index 05b9a286..bcec9af1 100644 --- a/nginx/src/event/quic/ngx_event_quic_migration.c +++ b/nginx/src/event/quic/ngx_event_quic_migration.c @@ -518,9 +518,7 @@ ngx_quic_validate_path(ngx_connection_t *c, ngx_quic_path_t *path) return NGX_ERROR; } - if (ngx_quic_send_path_challenge(c, path) != NGX_OK) { - return NGX_ERROR; - } + (void) ngx_quic_send_path_challenge(c, path); ctx = ngx_quic_get_send_ctx(qc, ssl_encryption_application); pto = ngx_max(ngx_quic_pto(c, ctx), 1000); diff --git a/nginx/src/event/quic/ngx_event_quic_openssl_compat.c b/nginx/src/event/quic/ngx_event_quic_openssl_compat.c index 318feda1..c7412e82 100644 --- a/nginx/src/event/quic/ngx_event_quic_openssl_compat.c +++ b/nginx/src/event/quic/ngx_event_quic_openssl_compat.c @@ -44,7 +44,6 @@ struct ngx_quic_compat_s { const SSL_QUIC_METHOD *method; enum ssl_encryption_level_t write_level; - enum ssl_encryption_level_t read_level; uint64_t read_record; ngx_quic_compat_keys_t keys; @@ -55,9 +54,10 @@ struct ngx_quic_compat_s { static void ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line); -static ngx_int_t ngx_quic_compat_set_encryption_secret(ngx_log_t *log, +static ngx_int_t ngx_quic_compat_set_encryption_secret(ngx_connection_t *c, ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len); +static void ngx_quic_compat_cleanup_encryption_secret(void *data); static int ngx_quic_compat_add_transport_params_callback(SSL *ssl, unsigned int ext_type, unsigned int context, const unsigned char **out, size_t *outlen, X509 *x, size_t chainidx, int *al, void *add_arg); @@ -213,66 +213,93 @@ ngx_quic_compat_keylog_callback(const SSL *ssl, const char *line) } else { com->method->set_read_secret((SSL *) ssl, level, cipher, secret, n); - com->read_level = level; com->read_record = 0; - (void) ngx_quic_compat_set_encryption_secret(c->log, &com->keys, level, + (void) ngx_quic_compat_set_encryption_secret(c, &com->keys, level, cipher, secret, n); } + + ngx_explicit_memzero(secret, n); } static ngx_int_t -ngx_quic_compat_set_encryption_secret(ngx_log_t *log, +ngx_quic_compat_set_encryption_secret(ngx_connection_t *c, ngx_quic_compat_keys_t *keys, enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len) { ngx_int_t key_len; ngx_str_t secret_str; ngx_uint_t i; + ngx_quic_md_t key; ngx_quic_hkdf_t seq[2]; ngx_quic_secret_t *peer_secret; ngx_quic_ciphers_t ciphers; + ngx_pool_cleanup_t *cln; peer_secret = &keys->secret; keys->cipher = SSL_CIPHER_get_id(cipher); - key_len = ngx_quic_ciphers(keys->cipher, &ciphers, level); + key_len = ngx_quic_ciphers(keys->cipher, &ciphers); if (key_len == NGX_ERROR) { - ngx_ssl_error(NGX_LOG_INFO, log, 0, "unexpected cipher"); - return NGX_ERROR; - } - - if (sizeof(peer_secret->secret.data) < secret_len) { - ngx_log_error(NGX_LOG_ALERT, log, 0, - "unexpected secret len: %uz", secret_len); + ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "unexpected cipher"); return NGX_ERROR; } - peer_secret->secret.len = secret_len; - ngx_memcpy(peer_secret->secret.data, secret, secret_len); + key.len = key_len; - peer_secret->key.len = key_len; peer_secret->iv.len = NGX_QUIC_IV_LEN; secret_str.len = secret_len; secret_str.data = (u_char *) secret; - ngx_quic_hkdf_set(&seq[0], "tls13 key", &peer_secret->key, &secret_str); + ngx_quic_hkdf_set(&seq[0], "tls13 key", &key, &secret_str); ngx_quic_hkdf_set(&seq[1], "tls13 iv", &peer_secret->iv, &secret_str); for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { - if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, log) != NGX_OK) { + if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, c->log) != NGX_OK) { + return NGX_ERROR; + } + } + + /* register cleanup handler once */ + + if (peer_secret->ctx) { + ngx_quic_crypto_cleanup(peer_secret); + + } else { + cln = ngx_pool_cleanup_add(c->pool, 0); + if (cln == NULL) { return NGX_ERROR; } + + cln->handler = ngx_quic_compat_cleanup_encryption_secret; + cln->data = peer_secret; } + if (ngx_quic_crypto_init(ciphers.c, peer_secret, &key, 1, c->log) + == NGX_ERROR) + { + return NGX_ERROR; + } + + ngx_explicit_memzero(key.data, key.len); + return NGX_OK; } +static void +ngx_quic_compat_cleanup_encryption_secret(void *data) +{ + ngx_quic_secret_t *secret = data; + + ngx_quic_crypto_cleanup(secret); +} + + static int ngx_quic_compat_add_transport_params_callback(SSL *ssl, unsigned int ext_type, unsigned int context, const unsigned char **out, size_t *outlen, X509 *x, @@ -410,7 +437,9 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type, "quic compat tx %s len:%uz ", ngx_quic_level_name(level), len); - (void) com->method->add_handshake_data(ssl, level, buf, len); + if (com->method->add_handshake_data(ssl, level, buf, len) != 1) { + goto failed; + } break; @@ -422,11 +451,19 @@ ngx_quic_compat_message_callback(int write_p, int version, int content_type, "quic compat %s alert:%ui len:%uz ", ngx_quic_level_name(level), alert, len); - (void) com->method->send_alert(ssl, level, alert); + if (com->method->send_alert(ssl, level, alert) != 1) { + goto failed; + } } break; } + + return; + +failed: + + ngx_post_event(&qc->close, &ngx_posted_events); } @@ -544,10 +581,9 @@ ngx_quic_compat_create_header(ngx_quic_compat_record_t *rec, u_char *out, static ngx_int_t ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec, ngx_str_t *res) { - ngx_str_t ad, out; - ngx_quic_secret_t *secret; - ngx_quic_ciphers_t ciphers; - u_char nonce[NGX_QUIC_IV_LEN]; + ngx_str_t ad, out; + ngx_quic_secret_t *secret; + u_char nonce[NGX_QUIC_IV_LEN]; ad.data = res->data; ad.len = ngx_quic_compat_create_header(rec, ad.data, 0); @@ -560,18 +596,12 @@ ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec, ngx_str_t *res) "quic compat ad len:%uz %xV", ad.len, &ad); #endif - if (ngx_quic_ciphers(rec->keys->cipher, &ciphers, rec->level) == NGX_ERROR) - { - return NGX_ERROR; - } - secret = &rec->keys->secret; ngx_memcpy(nonce, secret->iv.data, secret->iv.len); ngx_quic_compute_nonce(nonce, sizeof(nonce), rec->number); - if (ngx_quic_tls_seal(ciphers.c, secret, &out, - nonce, &rec->payload, &ad, rec->log) + if (ngx_quic_crypto_seal(secret, &out, nonce, &rec->payload, &ad, rec->log) != NGX_OK) { return NGX_ERROR; @@ -583,32 +613,6 @@ ngx_quic_compat_create_record(ngx_quic_compat_record_t *rec, ngx_str_t *res) } -enum ssl_encryption_level_t -SSL_quic_read_level(const SSL *ssl) -{ - ngx_connection_t *c; - ngx_quic_connection_t *qc; - - c = ngx_ssl_get_connection(ssl); - qc = ngx_quic_get_connection(c); - - return qc->compat->read_level; -} - - -enum ssl_encryption_level_t -SSL_quic_write_level(const SSL *ssl) -{ - ngx_connection_t *c; - ngx_quic_connection_t *qc; - - c = ngx_ssl_get_connection(ssl); - qc = ngx_quic_get_connection(c); - - return qc->compat->write_level; -} - - int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, size_t params_len) diff --git a/nginx/src/event/quic/ngx_event_quic_openssl_compat.h b/nginx/src/event/quic/ngx_event_quic_openssl_compat.h index b04f6e0b..77cc3cb0 100644 --- a/nginx/src/event/quic/ngx_event_quic_openssl_compat.h +++ b/nginx/src/event/quic/ngx_event_quic_openssl_compat.h @@ -48,8 +48,6 @@ ngx_int_t ngx_quic_compat_init(ngx_conf_t *cf, SSL_CTX *ctx); int SSL_set_quic_method(SSL *ssl, const SSL_QUIC_METHOD *quic_method); int SSL_provide_quic_data(SSL *ssl, enum ssl_encryption_level_t level, const uint8_t *data, size_t len); -enum ssl_encryption_level_t SSL_quic_read_level(const SSL *ssl); -enum ssl_encryption_level_t SSL_quic_write_level(const SSL *ssl); int SSL_set_quic_transport_params(SSL *ssl, const uint8_t *params, size_t params_len); void SSL_get_peer_quic_transport_params(const SSL *ssl, diff --git a/nginx/src/event/quic/ngx_event_quic_output.c b/nginx/src/event/quic/ngx_event_quic_output.c index 587671bc..dd528a7f 100644 --- a/nginx/src/event/quic/ngx_event_quic_output.c +++ b/nginx/src/event/quic/ngx_event_quic_output.c @@ -519,6 +519,21 @@ ngx_quic_output_packet(ngx_connection_t *c, ngx_quic_send_ctx_t *ctx, qc = ngx_quic_get_connection(c); + if (!ngx_quic_keys_available(qc->keys, ctx->level, 1)) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, "quic %s write keys discarded", + ngx_quic_level_name(ctx->level)); + + while (!ngx_queue_empty(&ctx->frames)) { + q = ngx_queue_head(&ctx->frames); + ngx_queue_remove(q); + + f = ngx_queue_data(q, ngx_quic_frame_t, queue); + ngx_quic_free_frame(c, f); + } + + return 0; + } + ngx_quic_init_packet(c, ctx, &pkt, qc->path); min_payload = ngx_quic_payload_size(&pkt, min); @@ -926,13 +941,17 @@ ngx_quic_send_early_cc(ngx_connection_t *c, ngx_quic_header_t *inpkt, res.data = dst; if (ngx_quic_encrypt(&pkt, &res) != NGX_OK) { + ngx_quic_keys_cleanup(pkt.keys); return NGX_ERROR; } if (ngx_quic_send(c, res.data, res.len, c->sockaddr, c->socklen) < 0) { + ngx_quic_keys_cleanup(pkt.keys); return NGX_ERROR; } + ngx_quic_keys_cleanup(pkt.keys); + return NGX_DONE; } diff --git a/nginx/src/event/quic/ngx_event_quic_protection.c b/nginx/src/event/quic/ngx_event_quic_protection.c index ecac6f57..88e6954c 100644 --- a/nginx/src/event/quic/ngx_event_quic_protection.c +++ b/nginx/src/event/quic/ngx_event_quic_protection.c @@ -15,6 +15,8 @@ #define NGX_QUIC_AES_128_KEY_LEN 16 +#define NGX_QUIC_INITIAL_CIPHER TLS1_3_CK_AES_128_GCM_SHA256 + static ngx_int_t ngx_hkdf_expand(u_char *out_key, size_t out_len, const EVP_MD *digest, const u_char *prk, size_t prk_len, @@ -26,11 +28,18 @@ static ngx_int_t ngx_hkdf_extract(u_char *out_key, size_t *out_len, static uint64_t ngx_quic_parse_pn(u_char **pos, ngx_int_t len, u_char *mask, uint64_t *largest_pn); -static ngx_int_t ngx_quic_tls_open(const ngx_quic_cipher_t *cipher, - ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in, - ngx_str_t *ad, ngx_log_t *log); -static ngx_int_t ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher, - ngx_quic_secret_t *s, u_char *out, u_char *in); +static ngx_int_t ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, + u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); +#ifndef OPENSSL_IS_BORINGSSL +static ngx_int_t ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, + u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); +#endif + +static ngx_int_t ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, + ngx_quic_secret_t *s, ngx_log_t *log); +static ngx_int_t ngx_quic_crypto_hp(ngx_quic_secret_t *s, + u_char *out, u_char *in, ngx_log_t *log); +static void ngx_quic_crypto_hp_cleanup(ngx_quic_secret_t *s); static ngx_int_t ngx_quic_create_packet(ngx_quic_header_t *pkt, ngx_str_t *res); @@ -39,15 +48,10 @@ static ngx_int_t ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_int_t -ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers, - enum ssl_encryption_level_t level) +ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers) { ngx_int_t len; - if (level == ssl_encryption_initial) { - id = TLS1_3_CK_AES_128_GCM_SHA256; - } - switch (id) { case TLS1_3_CK_AES_128_GCM_SHA256: @@ -108,13 +112,15 @@ ngx_int_t ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret, ngx_log_t *log) { - size_t is_len; - uint8_t is[SHA256_DIGEST_LENGTH]; - ngx_str_t iss; - ngx_uint_t i; - const EVP_MD *digest; - ngx_quic_hkdf_t seq[8]; - ngx_quic_secret_t *client, *server; + size_t is_len; + uint8_t is[SHA256_DIGEST_LENGTH]; + ngx_str_t iss; + ngx_uint_t i; + const EVP_MD *digest; + ngx_quic_md_t client_key, server_key; + ngx_quic_hkdf_t seq[8]; + ngx_quic_secret_t *client, *server; + ngx_quic_ciphers_t ciphers; static const uint8_t salt[20] = "\x38\x76\x2c\xf7\xf5\x59\x34\xb3\x4d\x17" @@ -155,8 +161,8 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret, client->secret.len = SHA256_DIGEST_LENGTH; server->secret.len = SHA256_DIGEST_LENGTH; - client->key.len = NGX_QUIC_AES_128_KEY_LEN; - server->key.len = NGX_QUIC_AES_128_KEY_LEN; + client_key.len = NGX_QUIC_AES_128_KEY_LEN; + server_key.len = NGX_QUIC_AES_128_KEY_LEN; client->hp.len = NGX_QUIC_AES_128_KEY_LEN; server->hp.len = NGX_QUIC_AES_128_KEY_LEN; @@ -166,11 +172,11 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret, /* labels per RFC 9001, 5.1. Packet Protection Keys */ ngx_quic_hkdf_set(&seq[0], "tls13 client in", &client->secret, &iss); - ngx_quic_hkdf_set(&seq[1], "tls13 quic key", &client->key, &client->secret); + ngx_quic_hkdf_set(&seq[1], "tls13 quic key", &client_key, &client->secret); ngx_quic_hkdf_set(&seq[2], "tls13 quic iv", &client->iv, &client->secret); ngx_quic_hkdf_set(&seq[3], "tls13 quic hp", &client->hp, &client->secret); ngx_quic_hkdf_set(&seq[4], "tls13 server in", &server->secret, &iss); - ngx_quic_hkdf_set(&seq[5], "tls13 quic key", &server->key, &server->secret); + ngx_quic_hkdf_set(&seq[5], "tls13 quic key", &server_key, &server->secret); ngx_quic_hkdf_set(&seq[6], "tls13 quic iv", &server->iv, &server->secret); ngx_quic_hkdf_set(&seq[7], "tls13 quic hp", &server->hp, &server->secret); @@ -180,7 +186,37 @@ ngx_quic_keys_set_initial_secret(ngx_quic_keys_t *keys, ngx_str_t *secret, } } + if (ngx_quic_ciphers(NGX_QUIC_INITIAL_CIPHER, &ciphers) == NGX_ERROR) { + return NGX_ERROR; + } + + if (ngx_quic_crypto_init(ciphers.c, client, &client_key, 0, log) + == NGX_ERROR) + { + return NGX_ERROR; + } + + if (ngx_quic_crypto_init(ciphers.c, server, &server_key, 1, log) + == NGX_ERROR) + { + goto failed; + } + + if (ngx_quic_crypto_hp_init(ciphers.hp, client, log) == NGX_ERROR) { + goto failed; + } + + if (ngx_quic_crypto_hp_init(ciphers.hp, server, log) == NGX_ERROR) { + goto failed; + } + return NGX_OK; + +failed: + + ngx_quic_keys_cleanup(keys); + + return NGX_ERROR; } @@ -343,33 +379,21 @@ ngx_hkdf_extract(u_char *out_key, size_t *out_len, const EVP_MD *digest, } -static ngx_int_t -ngx_quic_tls_open(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, - ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) +ngx_int_t +ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, + ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log) { #ifdef OPENSSL_IS_BORINGSSL EVP_AEAD_CTX *ctx; - ctx = EVP_AEAD_CTX_new(cipher, s->key.data, s->key.len, + ctx = EVP_AEAD_CTX_new(cipher, key->data, key->len, EVP_AEAD_DEFAULT_TAG_LENGTH); if (ctx == NULL) { ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_new() failed"); return NGX_ERROR; } - - if (EVP_AEAD_CTX_open(ctx, out->data, &out->len, out->len, nonce, s->iv.len, - in->data, in->len, ad->data, ad->len) - != 1) - { - EVP_AEAD_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_open() failed"); - return NGX_ERROR; - } - - EVP_AEAD_CTX_free(ctx); #else - int len; EVP_CIPHER_CTX *ctx; ctx = EVP_CIPHER_CTX_new(); @@ -378,17 +402,16 @@ ngx_quic_tls_open(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, return NGX_ERROR; } - if (EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) { + if (EVP_CipherInit_ex(ctx, cipher, NULL, NULL, NULL, enc) != 1) { EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptInit_ex() failed"); + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CipherInit_ex() failed"); return NGX_ERROR; } - in->len -= NGX_QUIC_TAG_LEN; - - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, NGX_QUIC_TAG_LEN, - in->data + in->len) - == 0) + if (EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE + && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, NGX_QUIC_TAG_LEN, + NULL) + == 0) { EVP_CIPHER_CTX_free(ctx); ngx_ssl_error(NGX_LOG_INFO, log, 0, @@ -405,213 +428,224 @@ ngx_quic_tls_open(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, return NGX_ERROR; } - if (EVP_DecryptInit_ex(ctx, NULL, NULL, s->key.data, nonce) != 1) { + if (EVP_CipherInit_ex(ctx, NULL, NULL, key->data, NULL, enc) != 1) { EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptInit_ex() failed"); - return NGX_ERROR; - } - - if (EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE - && EVP_DecryptUpdate(ctx, NULL, &len, NULL, in->len) != 1) - { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptUpdate() failed"); - return NGX_ERROR; - } - - if (EVP_DecryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptUpdate() failed"); + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CipherInit_ex() failed"); return NGX_ERROR; } +#endif - if (EVP_DecryptUpdate(ctx, out->data, &len, in->data, in->len) != 1) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptUpdate() failed"); - return NGX_ERROR; - } + s->ctx = ctx; + return NGX_OK; +} - out->len = len; - if (EVP_DecryptFinal_ex(ctx, out->data + out->len, &len) <= 0) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_DecryptFinal_ex failed"); +static ngx_int_t +ngx_quic_crypto_open(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, + ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) +{ +#ifdef OPENSSL_IS_BORINGSSL + if (EVP_AEAD_CTX_open(s->ctx, out->data, &out->len, out->len, nonce, + s->iv.len, in->data, in->len, ad->data, ad->len) + != 1) + { + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_open() failed"); return NGX_ERROR; } - out->len += len; - - EVP_CIPHER_CTX_free(ctx); -#endif - return NGX_OK; +#else + return ngx_quic_crypto_common(s, out, nonce, in, ad, log); +#endif } ngx_int_t -ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher, ngx_quic_secret_t *s, - ngx_str_t *out, u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) +ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, + ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) { - #ifdef OPENSSL_IS_BORINGSSL - EVP_AEAD_CTX *ctx; - - ctx = EVP_AEAD_CTX_new(cipher, s->key.data, s->key.len, - EVP_AEAD_DEFAULT_TAG_LENGTH); - if (ctx == NULL) { - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_new() failed"); - return NGX_ERROR; - } - - if (EVP_AEAD_CTX_seal(ctx, out->data, &out->len, out->len, nonce, s->iv.len, - in->data, in->len, ad->data, ad->len) + if (EVP_AEAD_CTX_seal(s->ctx, out->data, &out->len, out->len, nonce, + s->iv.len, in->data, in->len, ad->data, ad->len) != 1) { - EVP_AEAD_CTX_free(ctx); ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_AEAD_CTX_seal() failed"); return NGX_ERROR; } - EVP_AEAD_CTX_free(ctx); + return NGX_OK; #else - int len; - EVP_CIPHER_CTX *ctx; + return ngx_quic_crypto_common(s, out, nonce, in, ad, log); +#endif +} - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) { - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CIPHER_CTX_new() failed"); - return NGX_ERROR; - } - if (EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL) != 1) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptInit_ex() failed"); - return NGX_ERROR; - } +#ifndef OPENSSL_IS_BORINGSSL - if (EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE - && EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, NGX_QUIC_TAG_LEN, - NULL) - == 0) - { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, - "EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG) failed"); - return NGX_ERROR; - } +static ngx_int_t +ngx_quic_crypto_common(ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, + ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log) +{ + int len, enc; + ngx_quic_crypto_ctx_t *ctx; - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_IVLEN, s->iv.len, NULL) - == 0) - { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, - "EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_IVLEN) failed"); + ctx = s->ctx; + enc = EVP_CIPHER_CTX_encrypting(ctx); + + if (EVP_CipherInit_ex(ctx, NULL, NULL, NULL, nonce, enc) != 1) { + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CipherInit_ex() failed"); return NGX_ERROR; } - if (EVP_EncryptInit_ex(ctx, NULL, NULL, s->key.data, nonce) != 1) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptInit_ex() failed"); - return NGX_ERROR; + if (enc == 0) { + in->len -= NGX_QUIC_TAG_LEN; + + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, NGX_QUIC_TAG_LEN, + in->data + in->len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG) failed"); + return NGX_ERROR; + } } - if (EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE - && EVP_EncryptUpdate(ctx, NULL, &len, NULL, in->len) != 1) + if (EVP_CIPHER_mode(EVP_CIPHER_CTX_cipher(ctx)) == EVP_CIPH_CCM_MODE + && EVP_CipherUpdate(ctx, NULL, &len, NULL, in->len) != 1) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptUpdate() failed"); + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CipherUpdate() failed"); return NGX_ERROR; } - if (EVP_EncryptUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptUpdate() failed"); + if (EVP_CipherUpdate(ctx, NULL, &len, ad->data, ad->len) != 1) { + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CipherUpdate() failed"); return NGX_ERROR; } - if (EVP_EncryptUpdate(ctx, out->data, &len, in->data, in->len) != 1) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptUpdate() failed"); + if (EVP_CipherUpdate(ctx, out->data, &len, in->data, in->len) != 1) { + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CipherUpdate() failed"); return NGX_ERROR; } out->len = len; - if (EVP_EncryptFinal_ex(ctx, out->data + out->len, &len) <= 0) { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptFinal_ex failed"); + if (EVP_CipherFinal_ex(ctx, out->data + out->len, &len) <= 0) { + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CipherFinal_ex failed"); return NGX_ERROR; } out->len += len; - if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, NGX_QUIC_TAG_LEN, - out->data + out->len) - == 0) - { - EVP_CIPHER_CTX_free(ctx); - ngx_ssl_error(NGX_LOG_INFO, log, 0, - "EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_GET_TAG) failed"); - return NGX_ERROR; + if (enc == 1) { + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, NGX_QUIC_TAG_LEN, + out->data + out->len) + == 0) + { + ngx_ssl_error(NGX_LOG_INFO, log, 0, + "EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_GET_TAG) failed"); + return NGX_ERROR; + } + + out->len += NGX_QUIC_TAG_LEN; + } + + return NGX_OK; +} + +#endif + + +void +ngx_quic_crypto_cleanup(ngx_quic_secret_t *s) +{ + if (s->ctx) { +#ifdef OPENSSL_IS_BORINGSSL + EVP_AEAD_CTX_free(s->ctx); +#else + EVP_CIPHER_CTX_free(s->ctx); +#endif + s->ctx = NULL; } +} + - out->len += NGX_QUIC_TAG_LEN; +static ngx_int_t +ngx_quic_crypto_hp_init(const EVP_CIPHER *cipher, ngx_quic_secret_t *s, + ngx_log_t *log) +{ + EVP_CIPHER_CTX *ctx; - EVP_CIPHER_CTX_free(ctx); +#ifdef OPENSSL_IS_BORINGSSL + if (cipher == (EVP_CIPHER *) EVP_aead_chacha20_poly1305()) { + /* no EVP interface */ + s->hp_ctx = NULL; + return NGX_OK; + } #endif + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_CIPHER_CTX_new() failed"); + return NGX_ERROR; + } + + if (EVP_EncryptInit_ex(ctx, cipher, NULL, s->hp.data, NULL) != 1) { + EVP_CIPHER_CTX_free(ctx); + ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptInit_ex() failed"); + return NGX_ERROR; + } + + s->hp_ctx = ctx; return NGX_OK; } static ngx_int_t -ngx_quic_tls_hp(ngx_log_t *log, const EVP_CIPHER *cipher, - ngx_quic_secret_t *s, u_char *out, u_char *in) +ngx_quic_crypto_hp(ngx_quic_secret_t *s, u_char *out, u_char *in, + ngx_log_t *log) { int outlen; EVP_CIPHER_CTX *ctx; u_char zero[NGX_QUIC_HP_LEN] = {0}; + ctx = s->hp_ctx; + #ifdef OPENSSL_IS_BORINGSSL uint32_t cnt; - ngx_memcpy(&cnt, in, sizeof(uint32_t)); - - if (cipher == (const EVP_CIPHER *) EVP_aead_chacha20_poly1305()) { + if (ctx == NULL) { + ngx_memcpy(&cnt, in, sizeof(uint32_t)); CRYPTO_chacha_20(out, zero, NGX_QUIC_HP_LEN, s->hp.data, &in[4], cnt); return NGX_OK; } #endif - ctx = EVP_CIPHER_CTX_new(); - if (ctx == NULL) { - return NGX_ERROR; - } - - if (EVP_EncryptInit_ex(ctx, cipher, NULL, s->hp.data, in) != 1) { + if (EVP_EncryptInit_ex(ctx, NULL, NULL, NULL, in) != 1) { ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptInit_ex() failed"); - goto failed; + return NGX_ERROR; } if (!EVP_EncryptUpdate(ctx, out, &outlen, zero, NGX_QUIC_HP_LEN)) { ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptUpdate() failed"); - goto failed; + return NGX_ERROR; } if (!EVP_EncryptFinal_ex(ctx, out + NGX_QUIC_HP_LEN, &outlen)) { ngx_ssl_error(NGX_LOG_INFO, log, 0, "EVP_EncryptFinal_Ex() failed"); - goto failed; + return NGX_ERROR; } - EVP_CIPHER_CTX_free(ctx); - return NGX_OK; +} -failed: - - EVP_CIPHER_CTX_free(ctx); - return NGX_ERROR; +static void +ngx_quic_crypto_hp_cleanup(ngx_quic_secret_t *s) +{ + if (s->hp_ctx) { + EVP_CIPHER_CTX_free(s->hp_ctx); + s->hp_ctx = NULL; + } } @@ -623,6 +657,7 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write, ngx_int_t key_len; ngx_str_t secret_str; ngx_uint_t i; + ngx_quic_md_t key; ngx_quic_hkdf_t seq[3]; ngx_quic_secret_t *peer_secret; ngx_quic_ciphers_t ciphers; @@ -632,7 +667,7 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write, keys->cipher = SSL_CIPHER_get_id(cipher); - key_len = ngx_quic_ciphers(keys->cipher, &ciphers, level); + key_len = ngx_quic_ciphers(keys->cipher, &ciphers); if (key_len == NGX_ERROR) { ngx_ssl_error(NGX_LOG_INFO, log, 0, "unexpected cipher"); @@ -648,15 +683,14 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write, peer_secret->secret.len = secret_len; ngx_memcpy(peer_secret->secret.data, secret, secret_len); - peer_secret->key.len = key_len; + key.len = key_len; peer_secret->iv.len = NGX_QUIC_IV_LEN; peer_secret->hp.len = key_len; secret_str.len = secret_len; secret_str.data = (u_char *) secret; - ngx_quic_hkdf_set(&seq[0], "tls13 quic key", - &peer_secret->key, &secret_str); + ngx_quic_hkdf_set(&seq[0], "tls13 quic key", &key, &secret_str); ngx_quic_hkdf_set(&seq[1], "tls13 quic iv", &peer_secret->iv, &secret_str); ngx_quic_hkdf_set(&seq[2], "tls13 quic hp", &peer_secret->hp, &secret_str); @@ -666,15 +700,31 @@ ngx_quic_keys_set_encryption_secret(ngx_log_t *log, ngx_uint_t is_write, } } + if (ngx_quic_crypto_init(ciphers.c, peer_secret, &key, is_write, log) + == NGX_ERROR) + { + return NGX_ERROR; + } + + if (ngx_quic_crypto_hp_init(ciphers.hp, peer_secret, log) == NGX_ERROR) { + return NGX_ERROR; + } + + ngx_explicit_memzero(key.data, key.len); + return NGX_OK; } ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys, - enum ssl_encryption_level_t level) + enum ssl_encryption_level_t level, ngx_uint_t is_write) { - return keys->secrets[level].client.key.len != 0; + if (is_write == 0) { + return keys->secrets[level].client.ctx != NULL; + } + + return keys->secrets[level].server.ctx != NULL; } @@ -682,7 +732,19 @@ void ngx_quic_keys_discard(ngx_quic_keys_t *keys, enum ssl_encryption_level_t level) { - keys->secrets[level].client.key.len = 0; + ngx_quic_secret_t *client, *server; + + client = &keys->secrets[level].client; + server = &keys->secrets[level].server; + + ngx_quic_crypto_cleanup(client); + ngx_quic_crypto_cleanup(server); + + ngx_quic_crypto_hp_cleanup(client); + ngx_quic_crypto_hp_cleanup(server); + + ngx_explicit_memzero(client->secret.data, client->secret.len); + ngx_explicit_memzero(server->secret.data, server->secret.len); } @@ -694,73 +756,135 @@ ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys) current = &keys->secrets[ssl_encryption_application]; next = &keys->next_key; + ngx_quic_crypto_cleanup(¤t->client); + ngx_quic_crypto_cleanup(¤t->server); + tmp = *current; *current = *next; *next = tmp; } -ngx_int_t -ngx_quic_keys_update(ngx_connection_t *c, ngx_quic_keys_t *keys) +void +ngx_quic_keys_update(ngx_event_t *ev) { - ngx_uint_t i; - ngx_quic_hkdf_t seq[6]; - ngx_quic_ciphers_t ciphers; - ngx_quic_secrets_t *current, *next; + ngx_int_t key_len; + ngx_uint_t i; + ngx_quic_md_t client_key, server_key; + ngx_quic_hkdf_t seq[6]; + ngx_quic_keys_t *keys; + ngx_connection_t *c; + ngx_quic_ciphers_t ciphers; + ngx_quic_secrets_t *current, *next; + ngx_quic_connection_t *qc; + + c = ev->data; + qc = ngx_quic_get_connection(c); + keys = qc->keys; current = &keys->secrets[ssl_encryption_application]; next = &keys->next_key; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "quic key update"); - if (ngx_quic_ciphers(keys->cipher, &ciphers, ssl_encryption_application) - == NGX_ERROR) - { - return NGX_ERROR; + c->log->action = "updating keys"; + + key_len = ngx_quic_ciphers(keys->cipher, &ciphers); + + if (key_len == NGX_ERROR) { + goto failed; } + client_key.len = key_len; + server_key.len = key_len; + next->client.secret.len = current->client.secret.len; - next->client.key.len = current->client.key.len; next->client.iv.len = NGX_QUIC_IV_LEN; next->client.hp = current->client.hp; + next->client.hp_ctx = current->client.hp_ctx; next->server.secret.len = current->server.secret.len; - next->server.key.len = current->server.key.len; next->server.iv.len = NGX_QUIC_IV_LEN; next->server.hp = current->server.hp; + next->server.hp_ctx = current->server.hp_ctx; ngx_quic_hkdf_set(&seq[0], "tls13 quic ku", &next->client.secret, ¤t->client.secret); ngx_quic_hkdf_set(&seq[1], "tls13 quic key", - &next->client.key, &next->client.secret); + &client_key, &next->client.secret); ngx_quic_hkdf_set(&seq[2], "tls13 quic iv", &next->client.iv, &next->client.secret); ngx_quic_hkdf_set(&seq[3], "tls13 quic ku", &next->server.secret, ¤t->server.secret); ngx_quic_hkdf_set(&seq[4], "tls13 quic key", - &next->server.key, &next->server.secret); + &server_key, &next->server.secret); ngx_quic_hkdf_set(&seq[5], "tls13 quic iv", &next->server.iv, &next->server.secret); for (i = 0; i < (sizeof(seq) / sizeof(seq[0])); i++) { if (ngx_quic_hkdf_expand(&seq[i], ciphers.d, c->log) != NGX_OK) { - return NGX_ERROR; + goto failed; } } - return NGX_OK; + if (ngx_quic_crypto_init(ciphers.c, &next->client, &client_key, 0, c->log) + == NGX_ERROR) + { + goto failed; + } + + if (ngx_quic_crypto_init(ciphers.c, &next->server, &server_key, 1, c->log) + == NGX_ERROR) + { + goto failed; + } + + ngx_explicit_memzero(current->client.secret.data, + current->client.secret.len); + ngx_explicit_memzero(current->server.secret.data, + current->server.secret.len); + + ngx_explicit_memzero(client_key.data, client_key.len); + ngx_explicit_memzero(server_key.data, server_key.len); + + return; + +failed: + + ngx_quic_close_connection(c, NGX_ERROR); +} + + +void +ngx_quic_keys_cleanup(ngx_quic_keys_t *keys) +{ + ngx_uint_t i; + ngx_quic_secrets_t *next; + + for (i = 0; i < NGX_QUIC_ENCRYPTION_LAST; i++) { + ngx_quic_keys_discard(keys, i); + } + + next = &keys->next_key; + + ngx_quic_crypto_cleanup(&next->client); + ngx_quic_crypto_cleanup(&next->server); + + ngx_explicit_memzero(next->client.secret.data, + next->client.secret.len); + ngx_explicit_memzero(next->server.secret.data, + next->server.secret.len); } static ngx_int_t ngx_quic_create_packet(ngx_quic_header_t *pkt, ngx_str_t *res) { - u_char *pnp, *sample; - ngx_str_t ad, out; - ngx_uint_t i; - ngx_quic_secret_t *secret; - ngx_quic_ciphers_t ciphers; - u_char nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN]; + u_char *pnp, *sample; + ngx_str_t ad, out; + ngx_uint_t i; + ngx_quic_secret_t *secret; + u_char nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN]; ad.data = res->data; ad.len = ngx_quic_create_header(pkt, ad.data, &pnp); @@ -773,27 +897,19 @@ ngx_quic_create_packet(ngx_quic_header_t *pkt, ngx_str_t *res) "quic ad len:%uz %xV", ad.len, &ad); #endif - if (ngx_quic_ciphers(pkt->keys->cipher, &ciphers, pkt->level) == NGX_ERROR) - { - return NGX_ERROR; - } - secret = &pkt->keys->secrets[pkt->level].server; ngx_memcpy(nonce, secret->iv.data, secret->iv.len); ngx_quic_compute_nonce(nonce, sizeof(nonce), pkt->number); - if (ngx_quic_tls_seal(ciphers.c, secret, &out, - nonce, &pkt->payload, &ad, pkt->log) + if (ngx_quic_crypto_seal(secret, &out, nonce, &pkt->payload, &ad, pkt->log) != NGX_OK) { return NGX_ERROR; } sample = &out.data[4 - pkt->num_len]; - if (ngx_quic_tls_hp(pkt->log, ciphers.hp, secret, mask, sample) - != NGX_OK) - { + if (ngx_quic_crypto_hp(secret, mask, sample, pkt->log) != NGX_OK) { return NGX_ERROR; } @@ -815,11 +931,12 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res) { u_char *start; ngx_str_t ad, itag; + ngx_quic_md_t key; ngx_quic_secret_t secret; ngx_quic_ciphers_t ciphers; /* 5.8. Retry Packet Integrity */ - static u_char key[16] = + static u_char key_data[16] = "\xbe\x0c\x69\x0b\x9f\x66\x57\x5a\x1d\x76\x6b\x54\xe3\x68\xc8\x4e"; static u_char nonce[NGX_QUIC_IV_LEN] = "\x46\x15\x99\xd3\x5d\x63\x2b\xf2\x23\x98\x25\xbb"; @@ -836,20 +953,29 @@ ngx_quic_create_retry_packet(ngx_quic_header_t *pkt, ngx_str_t *res) "quic retry itag len:%uz %xV", ad.len, &ad); #endif - if (ngx_quic_ciphers(0, &ciphers, pkt->level) == NGX_ERROR) { + if (ngx_quic_ciphers(NGX_QUIC_INITIAL_CIPHER, &ciphers) == NGX_ERROR) { return NGX_ERROR; } - secret.key.len = sizeof(key); - ngx_memcpy(secret.key.data, key, sizeof(key)); + key.len = sizeof(key_data); + ngx_memcpy(key.data, key_data, sizeof(key_data)); secret.iv.len = NGX_QUIC_IV_LEN; - if (ngx_quic_tls_seal(ciphers.c, &secret, &itag, nonce, &in, &ad, pkt->log) + if (ngx_quic_crypto_init(ciphers.c, &secret, &key, 1, pkt->log) + == NGX_ERROR) + { + return NGX_ERROR; + } + + if (ngx_quic_crypto_seal(&secret, &itag, nonce, &in, &ad, pkt->log) != NGX_OK) { + ngx_quic_crypto_cleanup(&secret); return NGX_ERROR; } + ngx_quic_crypto_cleanup(&secret); + res->len = itag.data + itag.len - start; res->data = start; @@ -977,20 +1103,14 @@ ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_str_t *res) ngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn) { - u_char *p, *sample; - size_t len; - uint64_t pn, lpn; - ngx_int_t pnl, rc; - ngx_str_t in, ad; - ngx_uint_t key_phase; - ngx_quic_secret_t *secret; - ngx_quic_ciphers_t ciphers; - uint8_t nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN]; - - if (ngx_quic_ciphers(pkt->keys->cipher, &ciphers, pkt->level) == NGX_ERROR) - { - return NGX_ERROR; - } + u_char *p, *sample; + size_t len; + uint64_t pn, lpn; + ngx_int_t pnl; + ngx_str_t in, ad; + ngx_uint_t key_phase; + ngx_quic_secret_t *secret; + uint8_t nonce[NGX_QUIC_IV_LEN], mask[NGX_QUIC_HP_LEN]; secret = &pkt->keys->secrets[pkt->level].client; @@ -1014,9 +1134,7 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn) /* header protection */ - if (ngx_quic_tls_hp(pkt->log, ciphers.hp, secret, mask, sample) - != NGX_OK) - { + if (ngx_quic_crypto_hp(secret, mask, sample, pkt->log) != NGX_OK) { return NGX_DECLINED; } @@ -1069,9 +1187,9 @@ ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn) pkt->payload.len = in.len - NGX_QUIC_TAG_LEN; pkt->payload.data = pkt->plaintext + ad.len; - rc = ngx_quic_tls_open(ciphers.c, secret, &pkt->payload, - nonce, &in, &ad, pkt->log); - if (rc != NGX_OK) { + if (ngx_quic_crypto_open(secret, &pkt->payload, nonce, &in, &ad, pkt->log) + != NGX_OK) + { return NGX_DECLINED; } diff --git a/nginx/src/event/quic/ngx_event_quic_protection.h b/nginx/src/event/quic/ngx_event_quic_protection.h index 4e56ea9d..34cfee61 100644 --- a/nginx/src/event/quic/ngx_event_quic_protection.h +++ b/nginx/src/event/quic/ngx_event_quic_protection.h @@ -26,8 +26,10 @@ #ifdef OPENSSL_IS_BORINGSSL #define ngx_quic_cipher_t EVP_AEAD +#define ngx_quic_crypto_ctx_t EVP_AEAD_CTX #else #define ngx_quic_cipher_t EVP_CIPHER +#define ngx_quic_crypto_ctx_t EVP_CIPHER_CTX #endif @@ -45,9 +47,10 @@ typedef struct { typedef struct { ngx_quic_md_t secret; - ngx_quic_md_t key; ngx_quic_iv_t iv; ngx_quic_md_t hp; + ngx_quic_crypto_ctx_t *ctx; + EVP_CIPHER_CTX *hp_ctx; } ngx_quic_secret_t; @@ -95,19 +98,21 @@ ngx_int_t ngx_quic_keys_set_encryption_secret(ngx_log_t *log, enum ssl_encryption_level_t level, const SSL_CIPHER *cipher, const uint8_t *secret, size_t secret_len); ngx_uint_t ngx_quic_keys_available(ngx_quic_keys_t *keys, - enum ssl_encryption_level_t level); + enum ssl_encryption_level_t level, ngx_uint_t is_write); void ngx_quic_keys_discard(ngx_quic_keys_t *keys, enum ssl_encryption_level_t level); void ngx_quic_keys_switch(ngx_connection_t *c, ngx_quic_keys_t *keys); -ngx_int_t ngx_quic_keys_update(ngx_connection_t *c, ngx_quic_keys_t *keys); +void ngx_quic_keys_update(ngx_event_t *ev); +void ngx_quic_keys_cleanup(ngx_quic_keys_t *keys); ngx_int_t ngx_quic_encrypt(ngx_quic_header_t *pkt, ngx_str_t *res); ngx_int_t ngx_quic_decrypt(ngx_quic_header_t *pkt, uint64_t *largest_pn); void ngx_quic_compute_nonce(u_char *nonce, size_t len, uint64_t pn); -ngx_int_t ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers, - enum ssl_encryption_level_t level); -ngx_int_t ngx_quic_tls_seal(const ngx_quic_cipher_t *cipher, - ngx_quic_secret_t *s, ngx_str_t *out, u_char *nonce, ngx_str_t *in, - ngx_str_t *ad, ngx_log_t *log); +ngx_int_t ngx_quic_ciphers(ngx_uint_t id, ngx_quic_ciphers_t *ciphers); +ngx_int_t ngx_quic_crypto_init(const ngx_quic_cipher_t *cipher, + ngx_quic_secret_t *s, ngx_quic_md_t *key, ngx_int_t enc, ngx_log_t *log); +ngx_int_t ngx_quic_crypto_seal(ngx_quic_secret_t *s, ngx_str_t *out, + u_char *nonce, ngx_str_t *in, ngx_str_t *ad, ngx_log_t *log); +void ngx_quic_crypto_cleanup(ngx_quic_secret_t *s); ngx_int_t ngx_quic_hkdf_expand(ngx_quic_hkdf_t *hkdf, const EVP_MD *digest, ngx_log_t *log); diff --git a/nginx/src/event/quic/ngx_event_quic_socket.c b/nginx/src/event/quic/ngx_event_quic_socket.c index 8339de3f..c2bc822a 100644 --- a/nginx/src/event/quic/ngx_event_quic_socket.c +++ b/nginx/src/event/quic/ngx_event_quic_socket.c @@ -93,8 +93,8 @@ ngx_quic_open_sockets(ngx_connection_t *c, ngx_quic_connection_t *qc, tmp->sid.seqnum = NGX_QUIC_UNSET_PN; /* temporary socket */ - ngx_memcpy(tmp->sid.id, pkt->odcid.data, pkt->odcid.len); - tmp->sid.len = pkt->odcid.len; + ngx_memcpy(tmp->sid.id, pkt->dcid.data, pkt->dcid.len); + tmp->sid.len = pkt->dcid.len; if (ngx_quic_listen(c, qc, tmp) != NGX_OK) { goto failed; diff --git a/nginx/src/event/quic/ngx_event_quic_ssl.c b/nginx/src/event/quic/ngx_event_quic_ssl.c index e0862e29..7872783f 100644 --- a/nginx/src/event/quic/ngx_event_quic_ssl.c +++ b/nginx/src/event/quic/ngx_event_quic_ssl.c @@ -43,7 +43,8 @@ static int ngx_quic_add_handshake_data(ngx_ssl_conn_t *ssl_conn, static int ngx_quic_flush_flight(ngx_ssl_conn_t *ssl_conn); static int ngx_quic_send_alert(ngx_ssl_conn_t *ssl_conn, enum ssl_encryption_level_t level, uint8_t alert); -static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data); +static ngx_int_t ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data, + enum ssl_encryption_level_t level); #if (NGX_QUIC_BORINGSSL_API) @@ -354,7 +355,7 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, } if (f->offset == ctx->crypto.offset) { - if (ngx_quic_crypto_input(c, frame->data) != NGX_OK) { + if (ngx_quic_crypto_input(c, frame->data, pkt->level) != NGX_OK) { return NGX_ERROR; } @@ -372,7 +373,7 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, cl = ngx_quic_read_buffer(c, &ctx->crypto, (uint64_t) -1); if (cl) { - if (ngx_quic_crypto_input(c, cl) != NGX_OK) { + if (ngx_quic_crypto_input(c, cl, pkt->level) != NGX_OK) { return NGX_ERROR; } @@ -384,7 +385,8 @@ ngx_quic_handle_crypto_frame(ngx_connection_t *c, ngx_quic_header_t *pkt, static ngx_int_t -ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data) +ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data, + enum ssl_encryption_level_t level) { int n, sslerr; ngx_buf_t *b; @@ -397,17 +399,10 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data) ssl_conn = c->ssl->connection; - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic SSL_quic_read_level:%d SSL_quic_write_level:%d", - (int) SSL_quic_read_level(ssl_conn), - (int) SSL_quic_write_level(ssl_conn)); - for (cl = data; cl; cl = cl->next) { b = cl->buf; - if (!SSL_provide_quic_data(ssl_conn, SSL_quic_read_level(ssl_conn), - b->pos, b->last - b->pos)) - { + if (!SSL_provide_quic_data(ssl_conn, level, b->pos, b->last - b->pos)) { ngx_ssl_error(NGX_LOG_INFO, c->log, 0, "SSL_provide_quic_data() failed"); return NGX_ERROR; @@ -416,11 +411,6 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data) n = SSL_do_handshake(ssl_conn); - ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, - "quic SSL_quic_read_level:%d SSL_quic_write_level:%d", - (int) SSL_quic_read_level(ssl_conn), - (int) SSL_quic_write_level(ssl_conn)); - ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_do_handshake: %d", n); if (n <= 0) { @@ -444,7 +434,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data) } if (n <= 0 || SSL_in_init(ssl_conn)) { - if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data) + if (ngx_quic_keys_available(qc->keys, ssl_encryption_early_data, 0) && qc->client_tp_done) { if (ngx_quic_init_streams(c) != NGX_OK) { @@ -482,9 +472,7 @@ ngx_quic_crypto_input(ngx_connection_t *c, ngx_chain_t *data) * Generating next keys before a key update is received. */ - if (ngx_quic_keys_update(c, qc->keys) != NGX_OK) { - return NGX_ERROR; - } + ngx_post_event(&qc->key_update, &ngx_posted_events); /* * RFC 9001, 4.9.2. Discarding Handshake Keys diff --git a/nginx/src/event/quic/ngx_event_quic_streams.c b/nginx/src/event/quic/ngx_event_quic_streams.c index b4b0eda0..df04d0f0 100644 --- a/nginx/src/event/quic/ngx_event_quic_streams.c +++ b/nginx/src/event/quic/ngx_event_quic_streams.c @@ -630,6 +630,10 @@ ngx_quic_do_init_streams(ngx_connection_t *c) qc->streams.initialized = 1; + if (!qc->closing && qc->close.timer_set) { + ngx_del_timer(&qc->close); + } + return NGX_OK; } diff --git a/nginx/src/event/quic/ngx_event_quic_transport.c b/nginx/src/event/quic/ngx_event_quic_transport.c index fafe85f4..4e0324f4 100644 --- a/nginx/src/event/quic/ngx_event_quic_transport.c +++ b/nginx/src/event/quic/ngx_event_quic_transport.c @@ -1985,7 +1985,7 @@ ngx_quic_init_transport_params(ngx_quic_tp_t *tp, ngx_quic_conf_t *qcf) * tp->preferred_address = NULL */ - tp->max_idle_timeout = qcf->timeout; + tp->max_idle_timeout = qcf->idle_timeout; tp->max_udp_payload_size = NGX_QUIC_MAX_UDP_PAYLOAD_SIZE; diff --git a/nginx/src/http/modules/ngx_http_fastcgi_module.c b/nginx/src/http/modules/ngx_http_fastcgi_module.c index 2d9a18f9..b9890833 100644 --- a/nginx/src/http/modules/ngx_http_fastcgi_module.c +++ b/nginx/src/http/modules/ngx_http_fastcgi_module.c @@ -2048,7 +2048,10 @@ ngx_http_fastcgi_process_header(ngx_http_request_t *r) } u->headers_in.status_n = status; - u->headers_in.status_line = *status_line; + + if (status_line->len > 3) { + u->headers_in.status_line = *status_line; + } } else if (u->headers_in.location) { u->headers_in.status_n = 302; diff --git a/nginx/src/http/modules/ngx_http_scgi_module.c b/nginx/src/http/modules/ngx_http_scgi_module.c index 9fc18dcd..3acea87b 100644 --- a/nginx/src/http/modules/ngx_http_scgi_module.c +++ b/nginx/src/http/modules/ngx_http_scgi_module.c @@ -1153,7 +1153,10 @@ ngx_http_scgi_process_header(ngx_http_request_t *r) } u->headers_in.status_n = status; - u->headers_in.status_line = *status_line; + + if (status_line->len > 3) { + u->headers_in.status_line = *status_line; + } } else if (u->headers_in.location) { u->headers_in.status_n = 302; diff --git a/nginx/src/http/modules/ngx_http_uwsgi_module.c b/nginx/src/http/modules/ngx_http_uwsgi_module.c index e4f721bb..c1731ff4 100644 --- a/nginx/src/http/modules/ngx_http_uwsgi_module.c +++ b/nginx/src/http/modules/ngx_http_uwsgi_module.c @@ -1381,7 +1381,10 @@ ngx_http_uwsgi_process_header(ngx_http_request_t *r) } u->headers_in.status_n = status; - u->headers_in.status_line = *status_line; + + if (status_line->len > 3) { + u->headers_in.status_line = *status_line; + } } else if (u->headers_in.location) { u->headers_in.status_n = 302; diff --git a/nginx/src/http/ngx_http_request.h b/nginx/src/http/ngx_http_request.h index 92c54461..cc3b7c0a 100644 --- a/nginx/src/http/ngx_http_request.h +++ b/nginx/src/http/ngx_http_request.h @@ -324,10 +324,6 @@ typedef struct { #endif #endif -#if (NGX_HTTP_V3 || NGX_COMPAT) - ngx_http_v3_session_t *v3_session; -#endif - ngx_chain_t *busy; ngx_int_t nbusy; diff --git a/nginx/src/http/v2/ngx_http_v2.c b/nginx/src/http/v2/ngx_http_v2.c index 7c05ff1e..0f5bd3de 100644 --- a/nginx/src/http/v2/ngx_http_v2.c +++ b/nginx/src/http/v2/ngx_http_v2.c @@ -347,6 +347,7 @@ ngx_http_v2_read_handler(ngx_event_t *rev) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http2 read handler"); h2c->blocked = 1; + h2c->new_streams = 0; if (c->close) { c->close = 0; @@ -385,13 +386,11 @@ ngx_http_v2_read_handler(ngx_event_t *rev) h2mcf = ngx_http_get_module_main_conf(h2c->http_connection->conf_ctx, ngx_http_v2_module); - available = h2mcf->recv_buffer_size - 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE; + available = h2mcf->recv_buffer_size - NGX_HTTP_V2_STATE_BUFFER_SIZE; do { p = h2mcf->recv_buffer; - - ngx_memcpy(p, h2c->state.buffer, NGX_HTTP_V2_STATE_BUFFER_SIZE); - end = p + h2c->state.buffer_used; + end = ngx_cpymem(p, h2c->state.buffer, h2c->state.buffer_used); n = c->recv(c, end, available); @@ -1284,6 +1283,14 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, goto rst_stream; } + if (h2c->new_streams++ >= 2 * h2scf->concurrent_streams) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too many streams at once"); + + status = NGX_HTTP_V2_REFUSED_STREAM; + goto rst_stream; + } + if (!h2c->settings_ack && !(h2c->state.flags & NGX_HTTP_V2_END_STREAM_FLAG) && h2scf->preread_size < NGX_HTTP_V2_DEFAULT_WINDOW) @@ -1349,6 +1356,12 @@ ngx_http_v2_state_headers(ngx_http_v2_connection_t *h2c, u_char *pos, rst_stream: + if (h2c->refused_streams++ > ngx_max(h2scf->concurrent_streams, 100)) { + ngx_log_error(NGX_LOG_INFO, h2c->connection->log, 0, + "client sent too many refused streams"); + return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_NO_ERROR); + } + if (ngx_http_v2_send_rst_stream(h2c, h2c->state.sid, status) != NGX_OK) { return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } @@ -2577,7 +2590,7 @@ ngx_http_v2_state_save(ngx_http_v2_connection_t *h2c, u_char *pos, u_char *end, return ngx_http_v2_connection_error(h2c, NGX_HTTP_V2_INTERNAL_ERROR); } - ngx_memcpy(h2c->state.buffer, pos, NGX_HTTP_V2_STATE_BUFFER_SIZE); + ngx_memcpy(h2c->state.buffer, pos, size); h2c->state.buffer_used = size; h2c->state.handler = handler; diff --git a/nginx/src/http/v2/ngx_http_v2.h b/nginx/src/http/v2/ngx_http_v2.h index cb9014cc..6751b302 100644 --- a/nginx/src/http/v2/ngx_http_v2.h +++ b/nginx/src/http/v2/ngx_http_v2.h @@ -131,6 +131,8 @@ struct ngx_http_v2_connection_s { ngx_uint_t processing; ngx_uint_t frames; ngx_uint_t idle; + ngx_uint_t new_streams; + ngx_uint_t refused_streams; ngx_uint_t priority_limit; size_t send_window; diff --git a/nginx/src/http/v2/ngx_http_v2_module.c b/nginx/src/http/v2/ngx_http_v2_module.c index 62af9a54..d64488c2 100644 --- a/nginx/src/http/v2/ngx_http_v2_module.c +++ b/nginx/src/http/v2/ngx_http_v2_module.c @@ -388,7 +388,7 @@ ngx_http_v2_recv_buffer_size(ngx_conf_t *cf, void *post, void *data) { size_t *sp = data; - if (*sp <= 2 * NGX_HTTP_V2_STATE_BUFFER_SIZE) { + if (*sp <= NGX_HTTP_V2_STATE_BUFFER_SIZE) { return "value is too small"; } diff --git a/nginx/src/http/v3/ngx_http_v3.c b/nginx/src/http/v3/ngx_http_v3.c index eb86b2da..8db229b2 100644 --- a/nginx/src/http/v3/ngx_http_v3.c +++ b/nginx/src/http/v3/ngx_http_v3.c @@ -30,6 +30,8 @@ ngx_http_v3_init_session(ngx_connection_t *c) goto failed; } + h3c->http_connection = hc; + ngx_queue_init(&h3c->blocked); h3c->keepalive.log = c->log; @@ -48,7 +50,7 @@ ngx_http_v3_init_session(ngx_connection_t *c) cln->handler = ngx_http_v3_cleanup_session; cln->data = h3c; - hc->v3_session = h3c; + c->data = h3c; return NGX_OK; diff --git a/nginx/src/http/v3/ngx_http_v3.h b/nginx/src/http/v3/ngx_http_v3.h index 94b0d3e7..9dcb5e6a 100644 --- a/nginx/src/http/v3/ngx_http_v3.h +++ b/nginx/src/http/v3/ngx_http_v3.h @@ -78,11 +78,12 @@ #define NGX_HTTP_V3_ERR_DECODER_STREAM_ERROR 0x202 -#define ngx_http_quic_get_connection(c) \ - ((ngx_http_connection_t *) ((c)->quic ? (c)->quic->parent->data \ +#define ngx_http_v3_get_session(c) \ + ((ngx_http_v3_session_t *) ((c)->quic ? (c)->quic->parent->data \ : (c)->data)) -#define ngx_http_v3_get_session(c) ngx_http_quic_get_connection(c)->v3_session +#define ngx_http_quic_get_connection(c) \ + (ngx_http_v3_get_session(c)->http_connection) #define ngx_http_v3_get_module_loc_conf(c, module) \ ngx_http_get_module_loc_conf(ngx_http_quic_get_connection(c)->conf_ctx, \ @@ -120,6 +121,8 @@ struct ngx_http_v3_parse_s { struct ngx_http_v3_session_s { + ngx_http_connection_t *http_connection; + ngx_http_v3_dynamic_table_t table; ngx_event_t keepalive; diff --git a/nginx/src/http/v3/ngx_http_v3_module.c b/nginx/src/http/v3/ngx_http_v3_module.c index 875e5f29..139bd65f 100644 --- a/nginx/src/http/v3/ngx_http_v3_module.c +++ b/nginx/src/http/v3/ngx_http_v3_module.c @@ -192,7 +192,7 @@ ngx_http_v3_create_srv_conf(ngx_conf_t *cf) * h3scf->quic.host_key = { 0, NULL } * h3scf->quic.stream_reject_code_uni = 0; * h3scf->quic.disable_active_migration = 0; - * h3scf->quic.timeout = 0; + * h3scf->quic.idle_timeout = 0; * h3scf->max_blocked_streams = 0; */ @@ -223,7 +223,8 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_http_v3_srv_conf_t *prev = parent; ngx_http_v3_srv_conf_t *conf = child; - ngx_http_ssl_srv_conf_t *sscf; + ngx_http_ssl_srv_conf_t *sscf; + ngx_http_core_srv_conf_t *cscf; ngx_conf_merge_value(conf->enable, prev->enable, 1); @@ -281,6 +282,9 @@ ngx_http_v3_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_ERROR; } + cscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_core_module); + conf->quic.handshake_timeout = cscf->client_header_timeout; + sscf = ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module); conf->quic.ssl = &sscf->ssl; diff --git a/nginx/src/http/v3/ngx_http_v3_request.c b/nginx/src/http/v3/ngx_http_v3_request.c index 6f72dc40..87f5f321 100644 --- a/nginx/src/http/v3/ngx_http_v3_request.c +++ b/nginx/src/http/v3/ngx_http_v3_request.c @@ -58,30 +58,20 @@ static const struct { void ngx_http_v3_init_stream(ngx_connection_t *c) { - ngx_http_v3_session_t *h3c; ngx_http_connection_t *hc, *phc; ngx_http_v3_srv_conf_t *h3scf; ngx_http_core_loc_conf_t *clcf; - ngx_http_core_srv_conf_t *cscf; hc = c->data; hc->ssl = 1; clcf = ngx_http_get_module_loc_conf(hc->conf_ctx, ngx_http_core_module); - cscf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_core_module); - h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); if (c->quic == NULL) { - if (ngx_http_v3_init_session(c) != NGX_OK) { - ngx_http_close_connection(c); - return; - } - - h3c = hc->v3_session; - ngx_add_timer(&h3c->keepalive, cscf->client_header_timeout); + h3scf = ngx_http_get_module_srv_conf(hc->conf_ctx, ngx_http_v3_module); + h3scf->quic.idle_timeout = clcf->keepalive_timeout; - h3scf->quic.timeout = clcf->keepalive_timeout; ngx_quic_run(c, &h3scf->quic); return; } @@ -118,6 +108,10 @@ ngx_http_v3_init(ngx_connection_t *c) ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "http3 init"); + if (ngx_http_v3_init_session(c) != NGX_OK) { + return NGX_ERROR; + } + h3c = ngx_http_v3_get_session(c); clcf = ngx_http_v3_get_module_loc_conf(c, ngx_http_core_module); ngx_add_timer(&h3c->keepalive, clcf->keepalive_timeout);