From 3733c6fd70a9e9ce64901982621629a8cfcc66a8 Mon Sep 17 00:00:00 2001 From: Dmitry Volyntsev Date: Mon, 2 Mar 2020 20:07:36 +0300 Subject: Fixed premature background subrequest finalization. When "aio" or "aio threads" is used while processing the response body of an in-memory background subrequest, the subrequest could be finalized with an aio operation still in progress. Upon aio completion either parent request is woken or the old r->write_event_handler is called again. The latter may result in request errors. In either case post_subrequest handler is never called with the full response body, which is typically expected when using in-memory subrequests. Currently in nginx background subrequests are created by the upstream module and the mirror module. The issue does not manifest itself with these subrequests because they are header-only. But it can manifest itself with third-party modules which create in-memory background subrequests. --- src/http/ngx_http_request.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'src') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index bb69e71d0..5fcaa2c33 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2490,6 +2490,15 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) if (r != r->main) { clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (r->buffered || r->postponed) { + + if (ngx_http_set_write_handler(r) != NGX_OK) { + ngx_http_terminate_request(r, 0); + } + + return; + } + if (r->background) { if (!r->logged) { if (clcf->log_subrequest) { @@ -2509,15 +2518,6 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) return; } - if (r->buffered || r->postponed) { - - if (ngx_http_set_write_handler(r) != NGX_OK) { - ngx_http_terminate_request(r, 0); - } - - return; - } - pr = r->parent; if (r == c->data) { -- cgit From 76ac67b36f2db9acb2aeb4672f0aeaa8008e7d93 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 28 Feb 2020 19:54:13 +0300 Subject: Simplified subrequest finalization. Now it looks similar to what it was before background subrequests were introduced in 9552758a786e. --- src/http/ngx_http_request.c | 34 +++++++++++----------------------- 1 file changed, 11 insertions(+), 23 deletions(-) (limited to 'src') diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c index 5fcaa2c33..eb53996b1 100644 --- a/src/http/ngx_http_request.c +++ b/src/http/ngx_http_request.c @@ -2488,7 +2488,6 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) } if (r != r->main) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); if (r->buffered || r->postponed) { @@ -2499,32 +2498,14 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) return; } - if (r->background) { - if (!r->logged) { - if (clcf->log_subrequest) { - ngx_http_log_request(r); - } - - r->logged = 1; - - } else { - ngx_log_error(NGX_LOG_ALERT, c->log, 0, - "subrequest: \"%V?%V\" logged again", - &r->uri, &r->args); - } - - r->done = 1; - ngx_http_finalize_connection(r); - return; - } - pr = r->parent; - if (r == c->data) { - - r->main->count--; + if (r == c->data || r->background) { if (!r->logged) { + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + if (clcf->log_subrequest) { ngx_http_log_request(r); } @@ -2539,6 +2520,13 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc) r->done = 1; + if (r->background) { + ngx_http_finalize_connection(r); + return; + } + + r->main->count--; + if (pr->postponed && pr->postponed->request == r) { pr->postponed = pr->postponed->next; } -- cgit From 1688f575c2d84ce0cf0fb6fe6558e1b308358ffd Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Mon, 16 Mar 2020 12:41:41 +0300 Subject: Version bump. --- src/core/nginx.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/core/nginx.h b/src/core/nginx.h index 796c25c20..3db0c7f2f 100644 --- a/src/core/nginx.h +++ b/src/core/nginx.h @@ -9,8 +9,8 @@ #define _NGINX_H_INCLUDED_ -#define nginx_version 1017009 -#define NGINX_VERSION "1.17.9" +#define nginx_version 1017010 +#define NGINX_VERSION "1.17.10" #define NGINX_VER "nginx/" NGINX_VERSION #ifdef NGX_BUILD -- cgit From 65ae8b315211988a821bdc32050768f41571ddae Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Fri, 13 Mar 2020 02:12:10 +0300 Subject: Auth basic: explicitly zero out password buffer. --- src/http/modules/ngx_http_auth_basic_module.c | 37 +++++++++++++-------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/http/modules/ngx_http_auth_basic_module.c b/src/http/modules/ngx_http_auth_basic_module.c index a6f9ec46c..ed9df3430 100644 --- a/src/http/modules/ngx_http_auth_basic_module.c +++ b/src/http/modules/ngx_http_auth_basic_module.c @@ -25,7 +25,6 @@ static ngx_int_t ngx_http_auth_basic_crypt_handler(ngx_http_request_t *r, ngx_str_t *passwd, ngx_str_t *realm); static ngx_int_t ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm); -static void ngx_http_auth_basic_close(ngx_file_t *file); static void *ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf); static char *ngx_http_auth_basic_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child); @@ -177,8 +176,8 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r) offset); if (n == NGX_ERROR) { - ngx_http_auth_basic_close(&file); - return NGX_HTTP_INTERNAL_SERVER_ERROR; + rc = NGX_HTTP_INTERNAL_SERVER_ERROR; + goto cleanup; } if (n == 0) { @@ -219,12 +218,11 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r) if (buf[i] == LF || buf[i] == CR || buf[i] == ':') { buf[i] = '\0'; - ngx_http_auth_basic_close(&file); - pwd.len = i - passwd; pwd.data = &buf[passwd]; - return ngx_http_auth_basic_crypt_handler(r, &pwd, &realm); + rc = ngx_http_auth_basic_crypt_handler(r, &pwd, &realm); + goto cleanup; } break; @@ -251,8 +249,6 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r) offset += n; } - ngx_http_auth_basic_close(&file); - if (state == sw_passwd) { pwd.len = i - passwd; pwd.data = ngx_pnalloc(r->pool, pwd.len + 1); @@ -262,14 +258,26 @@ ngx_http_auth_basic_handler(ngx_http_request_t *r) ngx_cpystrn(pwd.data, &buf[passwd], pwd.len + 1); - return ngx_http_auth_basic_crypt_handler(r, &pwd, &realm); + rc = ngx_http_auth_basic_crypt_handler(r, &pwd, &realm); + goto cleanup; } ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "user \"%V\" was not found in \"%s\"", &r->headers_in.user, user_file.data); - return ngx_http_auth_basic_set_realm(r, &realm); + rc = ngx_http_auth_basic_set_realm(r, &realm); + +cleanup: + + if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, + ngx_close_file_n " \"%s\" failed", user_file.data); + } + + ngx_explicit_memzero(buf, NGX_HTTP_AUTH_BUF_SIZE); + + return rc; } @@ -338,15 +346,6 @@ ngx_http_auth_basic_set_realm(ngx_http_request_t *r, ngx_str_t *realm) return NGX_HTTP_UNAUTHORIZED; } -static void -ngx_http_auth_basic_close(ngx_file_t *file) -{ - if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ALERT, file->log, ngx_errno, - ngx_close_file_n " \"%s\" failed", file->name.data); - } -} - static void * ngx_http_auth_basic_create_loc_conf(ngx_conf_t *cf) -- cgit From b82c08f6102d65a5e5902e6fa85082e184a75003 Mon Sep 17 00:00:00 2001 From: Ruslan Ermilov Date: Wed, 8 Apr 2020 01:02:17 +0300 Subject: The new auth_delay directive for delaying unauthorized requests. The request processing is delayed by a timer. Since nginx updates internal time once at the start of each event loop iteration, this normally ensures constant time delay, adding a mitigation from time-based attacks. A notable exception to this is the case when there are no additional events before the timer expires. To ensure constant-time processing in this case as well, we trigger an additional event loop iteration by posting a dummy event for the next event loop iteration. --- src/http/ngx_http_core_module.c | 82 ++++++++++++++++++++++++++++++++++++++++- src/http/ngx_http_core_module.h | 1 + 2 files changed, 82 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c index 4867bed2b..3671558d8 100644 --- a/src/http/ngx_http_core_module.c +++ b/src/http/ngx_http_core_module.c @@ -21,6 +21,9 @@ typedef struct { #define NGX_HTTP_REQUEST_BODY_FILE_CLEAN 2 +static ngx_int_t ngx_http_core_auth_delay(ngx_http_request_t *r); +static void ngx_http_core_auth_delay_handler(ngx_http_request_t *r); + static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r); static ngx_int_t ngx_http_core_find_static_location(ngx_http_request_t *r, ngx_http_location_tree_node_t *node); @@ -520,6 +523,13 @@ static ngx_command_t ngx_http_core_commands[] = { offsetof(ngx_http_core_loc_conf_t, satisfy), &ngx_http_core_satisfy }, + { ngx_string("auth_delay"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, auth_delay), + NULL }, + { ngx_string("internal"), NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS, ngx_http_core_internal, @@ -1124,6 +1134,10 @@ ngx_http_core_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) /* rc == NGX_ERROR || rc == NGX_HTTP_... */ + if (rc == NGX_HTTP_UNAUTHORIZED) { + return ngx_http_core_auth_delay(r); + } + ngx_http_finalize_request(r, rc); return NGX_OK; } @@ -1141,12 +1155,17 @@ ngx_http_core_post_access_phase(ngx_http_request_t *r, access_code = r->access_code; if (access_code) { + r->access_code = 0; + if (access_code == NGX_HTTP_FORBIDDEN) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "access forbidden by rule"); } - r->access_code = 0; + if (access_code == NGX_HTTP_UNAUTHORIZED) { + return ngx_http_core_auth_delay(r); + } + ngx_http_finalize_request(r, access_code); return NGX_OK; } @@ -1156,6 +1175,65 @@ ngx_http_core_post_access_phase(ngx_http_request_t *r, } +static ngx_int_t +ngx_http_core_auth_delay(ngx_http_request_t *r) +{ + ngx_http_core_loc_conf_t *clcf; + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->auth_delay == 0) { + ngx_http_finalize_request(r, NGX_HTTP_UNAUTHORIZED); + return NGX_OK; + } + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, + "delaying unauthorized request"); + + if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->read_event_handler = ngx_http_test_reading; + r->write_event_handler = ngx_http_core_auth_delay_handler; + + r->connection->write->delayed = 1; + ngx_add_timer(r->connection->write, clcf->auth_delay); + + /* + * trigger an additional event loop iteration + * to ensure constant-time processing + */ + + ngx_post_event(r->connection->write, &ngx_posted_next_events); + + return NGX_OK; +} + + +static void +ngx_http_core_auth_delay_handler(ngx_http_request_t *r) +{ + ngx_event_t *wev; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "auth delay handler"); + + wev = r->connection->write; + + if (wev->delayed) { + + if (ngx_handle_write_event(wev, 0) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + } + + return; + } + + ngx_http_finalize_request(r, NGX_HTTP_UNAUTHORIZED); +} + + ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) @@ -3394,6 +3472,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf) clcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; clcf->client_body_timeout = NGX_CONF_UNSET_MSEC; clcf->satisfy = NGX_CONF_UNSET_UINT; + clcf->auth_delay = NGX_CONF_UNSET_MSEC; clcf->if_modified_since = NGX_CONF_UNSET_UINT; clcf->max_ranges = NGX_CONF_UNSET_UINT; clcf->client_body_in_file_only = NGX_CONF_UNSET_UINT; @@ -3609,6 +3688,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) |NGX_HTTP_KEEPALIVE_DISABLE_MSIE6)); ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy, NGX_HTTP_SATISFY_ALL); + ngx_conf_merge_msec_value(conf->auth_delay, prev->auth_delay, 0); ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since, NGX_HTTP_IMS_EXACT); ngx_conf_merge_uint_value(conf->max_ranges, prev->max_ranges, diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h index f5434cc51..2aadae7ff 100644 --- a/src/http/ngx_http_core_module.h +++ b/src/http/ngx_http_core_module.h @@ -363,6 +363,7 @@ struct ngx_http_core_loc_conf_s { ngx_msec_t lingering_time; /* lingering_time */ ngx_msec_t lingering_timeout; /* lingering_timeout */ ngx_msec_t resolver_timeout; /* resolver_timeout */ + ngx_msec_t auth_delay; /* auth_delay */ ngx_resolver_t *resolver; /* resolver */ -- cgit