lighttpd: patches from upstreamlilik-openwrt-22.03
@ -0,0 +1,31 @@ | |||
From 1ca25d4e2cfeb83c844ad52b9c94eac218c71379 Mon Sep 17 00:00:00 2001 | |||
From: Glenn Strauss <gstrauss@gluelogic.com> | |||
Date: Thu, 4 Feb 2021 00:22:12 -0500 | |||
Subject: [PATCH] [core] 101 upgrade fails if Content-Length incl (fixes #3063) | |||
(thx daimh) | |||
commit 903024d7 in lighttpd 1.4.57 fixed issue #3046 but in the process | |||
broke HTTP/1.1 101 Switching Protocols which included Content-Length: 0 | |||
in the response headers. Content-Length response header is permitted | |||
by the RFCs, but not necessary with HTTP status 101 Switching Protocols. | |||
x-ref: | |||
"websocket proxy fails if 101 Switching Protocols from backend includes Content-Length" | |||
https://redmine.lighttpd.net/issues/3063 | |||
Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com> | |||
--- | |||
src/http-header-glue.c | 1 + | |||
1 file changed, 1 insertion(+) | |||
--- a/src/http-header-glue.c | |||
+++ b/src/http-header-glue.c | |||
@@ -961,6 +961,7 @@ void http_response_upgrade_read_body_unk | |||
(FDEVENT_STREAM_RESPONSE_BUFMIN | FDEVENT_STREAM_RESPONSE); | |||
r->conf.stream_request_body |= FDEVENT_STREAM_REQUEST_POLLIN; | |||
r->reqbody_length = -2; | |||
+ r->resp_body_scratchpad = -1; | |||
r->keep_alive = 0; | |||
} | |||
@ -0,0 +1,143 @@ | |||
From 4a600dabd5e2799bf0c3048859ee4f00808b7d89 Mon Sep 17 00:00:00 2001 | |||
From: Glenn Strauss <gstrauss@gluelogic.com> | |||
Date: Sat, 6 Feb 2021 08:29:41 -0500 | |||
Subject: [PATCH] [mod_auth] close HTTP/2 connection after bad pass | |||
mitigation slows down brute force password attacks | |||
x-ref: | |||
"Possible feature: authentication brute force hardening" | |||
https://redmine.lighttpd.net/boards/3/topics/8885 | |||
Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com> | |||
--- | |||
src/connections.c | 22 +++++++++++++++++++++- | |||
src/mod_accesslog.c | 2 +- | |||
src/mod_auth.c | 6 +++--- | |||
src/reqpool.c | 1 + | |||
src/request.h | 2 +- | |||
src/response.c | 4 ++-- | |||
6 files changed, 29 insertions(+), 8 deletions(-) | |||
--- a/src/connections.c | |||
+++ b/src/connections.c | |||
@@ -228,7 +228,7 @@ static void connection_handle_response_e | |||
} | |||
} | |||
- if (r->keep_alive) { | |||
+ if (r->keep_alive > 0) { | |||
request_reset(r); | |||
config_reset_config(r); | |||
con->is_readable = 1; /* potentially trigger optimistic read */ | |||
@@ -1265,6 +1265,19 @@ connection_set_fdevent_interest (request | |||
} | |||
+__attribute_cold__ | |||
+static void | |||
+connection_request_end_h2 (request_st * const h2r, connection * const con) | |||
+{ | |||
+ if (h2r->keep_alive >= 0) { | |||
+ h2r->keep_alive = -1; | |||
+ h2_send_goaway(con, H2_E_NO_ERROR); | |||
+ } | |||
+ else /*(abort connection upon second request to close h2 connection)*/ | |||
+ h2_send_goaway(con, H2_E_ENHANCE_YOUR_CALM); | |||
+} | |||
+ | |||
+ | |||
static void | |||
connection_state_machine_h2 (request_st * const h2r, connection * const con) | |||
{ | |||
@@ -1359,8 +1372,15 @@ connection_state_machine_h2 (request_st | |||
&& !chunkqueue_is_empty(con->read_queue)) | |||
resched |= 1; | |||
h2_send_end_stream(r, con); | |||
+ const int alive = r->keep_alive; | |||
h2_retire_stream(r, con);/*r invalidated;removed from h2c->r[]*/ | |||
--i;/* adjust loop i; h2c->rused was modified to retire r */ | |||
+ /*(special-case: allow *stream* to set r->keep_alive = -1 to | |||
+ * trigger goaway on h2 connection, e.g. after mod_auth failure | |||
+ * in attempt to mitigate brute force attacks by forcing a | |||
+ * reconnect and (somewhat) slowing down retries)*/ | |||
+ if (alive < 0) | |||
+ connection_request_end_h2(h2r, con); | |||
} | |||
} | |||
} | |||
--- a/src/mod_accesslog.c | |||
+++ b/src/mod_accesslog.c | |||
@@ -1108,7 +1108,7 @@ static int log_access_record (const requ | |||
break; | |||
case FORMAT_CONNECTION_STATUS: | |||
if (r->state == CON_STATE_RESPONSE_END) { | |||
- if (0 == r->keep_alive) { | |||
+ if (r->keep_alive <= 0) { | |||
buffer_append_string_len(b, CONST_STR_LEN("-")); | |||
} else { | |||
buffer_append_string_len(b, CONST_STR_LEN("+")); | |||
--- a/src/mod_auth.c | |||
+++ b/src/mod_auth.c | |||
@@ -828,7 +828,7 @@ static handler_t mod_auth_check_basic(re | |||
log_error(r->conf.errh, __FILE__, __LINE__, | |||
"password doesn't match for %s username: %s IP: %s", | |||
r->uri.path.ptr, username->ptr, r->con->dst_addr_buf->ptr); | |||
- r->keep_alive = 0; /*(disable keep-alive if bad password)*/ | |||
+ r->keep_alive = -1; /*(disable keep-alive if bad password)*/ | |||
rc = HANDLER_UNSET; | |||
break; | |||
} | |||
@@ -1461,7 +1461,7 @@ static handler_t mod_auth_check_digest(r | |||
return HANDLER_FINISHED; | |||
case HANDLER_ERROR: | |||
default: | |||
- r->keep_alive = 0; /*(disable keep-alive if unknown user)*/ | |||
+ r->keep_alive = -1; /*(disable keep-alive if unknown user)*/ | |||
buffer_free(b); | |||
return mod_auth_send_401_unauthorized_digest(r, require, 0); | |||
} | |||
@@ -1482,7 +1482,7 @@ static handler_t mod_auth_check_digest(r | |||
log_error(r->conf.errh, __FILE__, __LINE__, | |||
"digest: auth failed for %s: wrong password, IP: %s", | |||
username, r->con->dst_addr_buf->ptr); | |||
- r->keep_alive = 0; /*(disable keep-alive if bad password)*/ | |||
+ r->keep_alive = -1; /*(disable keep-alive if bad password)*/ | |||
buffer_free(b); | |||
return mod_auth_send_401_unauthorized_digest(r, require, 0); | |||
--- a/src/reqpool.c | |||
+++ b/src/reqpool.c | |||
@@ -58,6 +58,7 @@ request_reset (request_st * const r) | |||
http_response_reset(r); | |||
r->loops_per_request = 0; | |||
+ r->keep_alive = 0; | |||
r->h2state = 0; /* H2_STATE_IDLE */ | |||
r->h2id = 0; | |||
--- a/src/request.h | |||
+++ b/src/request.h | |||
@@ -175,7 +175,7 @@ struct request_st { | |||
char resp_header_repeated; | |||
char loops_per_request; /* catch endless loops in a single request */ | |||
- char keep_alive; /* only request.c can enable it, all other just disable */ | |||
+ int8_t keep_alive; /* only request.c can enable it, all other just disable */ | |||
char async_callback; | |||
buffer *tmp_buf; /* shared; same as srv->tmp_buf */ | |||
--- a/src/response.c | |||
+++ b/src/response.c | |||
@@ -103,9 +103,9 @@ http_response_write_header (request_st * | |||
if (light_btst(r->resp_htags, HTTP_HEADER_UPGRADE) | |||
&& r->http_version == HTTP_VERSION_1_1) { | |||
http_header_response_set(r, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("upgrade")); | |||
- } else if (0 == r->keep_alive) { | |||
+ } else if (r->keep_alive <= 0) { | |||
http_header_response_set(r, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("close")); | |||
- } else if (r->http_version == HTTP_VERSION_1_0) {/*(&& r->keep_alive != 0)*/ | |||
+ } else if (r->http_version == HTTP_VERSION_1_0) {/*(&& r->keep_alive > 0)*/ | |||
http_header_response_set(r, HTTP_HEADER_CONNECTION, CONST_STR_LEN("Connection"), CONST_STR_LEN("keep-alive")); | |||
} | |||
@ -0,0 +1,45 @@ | |||
From aa81834bc3ff47aa5cc66b6763678d3cf47a3d54 Mon Sep 17 00:00:00 2001 | |||
From: Glenn Strauss <gstrauss@gluelogic.com> | |||
Date: Fri, 12 Mar 2021 20:03:38 -0500 | |||
Subject: [PATCH] [mod_openssl] skip cert chain build if self-issued | |||
If cert is self-issued, then do not attempt to build certificate chain. | |||
(Attempting to build certificate chain when chain is not provided, but | |||
ssl.ca-file is specified, is provided as backward compatible behavior | |||
from lighttpd versions prior to lighttpd 1.4.56) | |||
Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com> | |||
--- | |||
src/mod_openssl.c | 6 +++++- | |||
1 file changed, 5 insertions(+), 1 deletion(-) | |||
--- a/src/mod_openssl.c | |||
+++ b/src/mod_openssl.c | |||
@@ -103,6 +103,7 @@ typedef struct { | |||
time_t ssl_stapling_loadts; | |||
time_t ssl_stapling_nextts; | |||
char must_staple; | |||
+ char self_issued; | |||
} plugin_cert; | |||
typedef struct { | |||
@@ -1081,7 +1082,7 @@ mod_openssl_cert_cb (SSL *ssl, void *arg | |||
#if !defined(BORINGSSL_API_VERSION) \ | |||
&& !defined(LIBRESSL_VERSION_NUMBER) | |||
/* (missing SSL_set1_chain_cert_store() and SSL_build_cert_chain()) */ | |||
- else if (hctx->conf.ssl_ca_file) { | |||
+ else if (hctx->conf.ssl_ca_file && !pc->self_issued) { | |||
/* preserve legacy behavior whereby openssl will reuse CAs trusted for | |||
* certificate verification (set by SSL_CTX_load_verify_locations() in | |||
* SSL_CTX) in order to build certificate chain for server certificate | |||
@@ -1671,6 +1672,9 @@ network_openssl_load_pemfile (server *sr | |||
#else | |||
pc->must_staple = 0; | |||
#endif | |||
+ pc->self_issued = | |||
+ (0 == X509_NAME_cmp(X509_get_subject_name(ssl_pemfile_x509), | |||
+ X509_get_issuer_name(ssl_pemfile_x509))); | |||
if (!buffer_string_is_empty(pc->ssl_stapling_file)) { | |||
#ifndef OPENSSL_NO_OCSP |
@ -0,0 +1,27 @@ | |||
From c41ebea4bb220c8fe252f472eec836c691734690 Mon Sep 17 00:00:00 2001 | |||
From: Glenn Strauss <gstrauss@gluelogic.com> | |||
Date: Fri, 2 Apr 2021 01:01:02 -0400 | |||
Subject: [PATCH] [build] fix zstd option in meson (fixes #3076) | |||
(thx KimonHoffmann) | |||
x-ref: | |||
"Fix zstd dependency handling in meson build" | |||
https://redmine.lighttpd.net/issues/3076 | |||
Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com> | |||
--- | |||
src/meson.build | 2 +- | |||
1 file changed, 1 insertion(+), 1 deletion(-) | |||
--- a/src/meson.build | |||
+++ b/src/meson.build | |||
@@ -685,7 +685,7 @@ endif | |||
libzstd = [] | |||
if get_option('with_zstd') | |||
- libz = dependency('zstd', required: false) | |||
+ libzstd = dependency('zstd', required: false) | |||
if libzstd.found() | |||
libzstd = [ libzstd ] | |||
else |
@ -0,0 +1,56 @@ | |||
From 3392e8fb11de35778cad1fb112e6eb5916aa7de0 Mon Sep 17 00:00:00 2001 | |||
From: Glenn Strauss <gstrauss@gluelogic.com> | |||
Date: Tue, 20 Apr 2021 22:04:56 -0400 | |||
Subject: [PATCH] [core] update ls-hpack | |||
LiteSpeed ls-hpack v2.3.0 | |||
Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com> | |||
--- | |||
src/ls-hpack/README.md | 2 +- | |||
src/ls-hpack/lshpack.c | 4 +++- | |||
src/ls-hpack/lshpack.h | 6 +++--- | |||
3 files changed, 7 insertions(+), 5 deletions(-) | |||
--- a/src/ls-hpack/lshpack.c | |||
+++ b/src/ls-hpack/lshpack.c | |||
@@ -1,7 +1,7 @@ | |||
/* | |||
MIT License | |||
-Copyright (c) 2018 LiteSpeed Technologies Inc | |||
+Copyright (c) 2018 - 2021 LiteSpeed Technologies Inc | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
@@ -1549,6 +1549,8 @@ lshpack_dec_push_entry (struct lshpack_d | |||
#endif | |||
memcpy(DTE_NAME(entry), lsxpack_header_get_name(xhdr), name_len); | |||
memcpy(DTE_VALUE(entry), lsxpack_header_get_value(xhdr), val_len); | |||
+ | |||
+ hdec_remove_overflow_entries(dec); | |||
return 0; | |||
} | |||
--- a/src/ls-hpack/lshpack.h | |||
+++ b/src/ls-hpack/lshpack.h | |||
@@ -1,7 +1,7 @@ | |||
/* | |||
MIT License | |||
-Copyright (c) 2018 - 2020 LiteSpeed Technologies Inc | |||
+Copyright (c) 2018 - 2021 LiteSpeed Technologies Inc | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
@@ -34,8 +34,8 @@ extern "C" { | |||
#include "lsxpack_header.h" | |||
#define LSHPACK_MAJOR_VERSION 2 | |||
-#define LSHPACK_MINOR_VERSION 2 | |||
-#define LSHPACK_PATCH_VERSION 1 | |||
+#define LSHPACK_MINOR_VERSION 3 | |||
+#define LSHPACK_PATCH_VERSION 0 | |||
#define lshpack_strlen_t lsxpack_strlen_t | |||
#define LSHPACK_MAX_STRLEN LSXPACK_MAX_STRLEN |
@ -0,0 +1,145 @@ | |||
From 81d18a8e359685c169cfd30e6a1574b98aedbaeb Mon Sep 17 00:00:00 2001 | |||
From: Glenn Strauss <gstrauss@gluelogic.com> | |||
Date: Thu, 22 Apr 2021 01:11:47 -0400 | |||
Subject: [PATCH] [core] discard some HTTP/2 DATA after response (fixes #3078) | |||
(thx oldium) | |||
improve handling of HTTP/2 DATA frames received | |||
a short time after sending response | |||
x-ref: | |||
"POST request DATA part for non-existing URI closes HTTP/2 connection prematurely" | |||
https://redmine.lighttpd.net/issues/3078 | |||
Signed-off-by: Glenn Strauss <gstrauss@gluelogic.com> | |||
--- | |||
src/h2.c | 64 ++++++++++++++++++++++++++++++++++++++++++-------------- | |||
src/h2.h | 1 + | |||
2 files changed, 49 insertions(+), 16 deletions(-) | |||
--- a/src/h2.c | |||
+++ b/src/h2.c | |||
@@ -272,10 +272,23 @@ h2_send_rst_stream_id (uint32_t h2id, co | |||
__attribute_cold__ | |||
static void | |||
-h2_send_rst_stream (request_st * const r, connection * const con, const request_h2error_t e) | |||
+h2_send_rst_stream_state (request_st * const r, h2con * const h2c) | |||
{ | |||
+ if (r->h2state != H2_STATE_HALF_CLOSED_REMOTE | |||
+ && r->h2state != H2_STATE_CLOSED) { | |||
+ /* set timestamp for comparison; not tracking individual stream ids */ | |||
+ h2c->half_closed_ts = log_epoch_secs; | |||
+ } | |||
r->state = CON_STATE_ERROR; | |||
r->h2state = H2_STATE_CLOSED; | |||
+} | |||
+ | |||
+ | |||
+__attribute_cold__ | |||
+static void | |||
+h2_send_rst_stream (request_st * const r, connection * const con, const request_h2error_t e) | |||
+{ | |||
+ h2_send_rst_stream_state(r, con->h2);/*(sets r->h2state = H2_STATE_CLOSED)*/ | |||
h2_send_rst_stream_id(r->h2id, con, e); | |||
} | |||
@@ -289,13 +302,10 @@ h2_send_goaway_rst_stream (connection * | |||
for (uint32_t i = 0, rused = h2c->rused; i < rused; ++i) { | |||
request_st * const r = h2c->r[i]; | |||
if (r->h2state == H2_STATE_CLOSED) continue; | |||
+ h2_send_rst_stream_state(r, h2c);/*(sets r->h2state = H2_STATE_CLOSED)*/ | |||
/*(XXX: might consider always sending RST_STREAM)*/ | |||
- if (!sent_goaway) { | |||
- r->state = CON_STATE_ERROR; | |||
- r->h2state = H2_STATE_CLOSED; | |||
- } | |||
- else /*(also sets r->h2state = H2_STATE_CLOSED)*/ | |||
- h2_send_rst_stream(r, con, H2_E_PROTOCOL_ERROR); | |||
+ if (sent_goaway) | |||
+ h2_send_rst_stream_id(r->h2id, con, H2_E_PROTOCOL_ERROR); | |||
} | |||
} | |||
@@ -780,14 +790,27 @@ h2_recv_data (connection * const con, co | |||
} | |||
chunkqueue * const cq = con->read_queue; | |||
if (NULL == r) { | |||
- /* XXX: TODO: might need to keep a list of recently retired streams | |||
- * for a few seconds so that if we send RST_STREAM, then we ignore | |||
- * further DATA and do not send connection error, though recv windows | |||
- * still must be updated. */ | |||
- if (h2c->h2_cid < id || (!h2c->sent_goaway && 0 != alen)) | |||
- h2_send_goaway_e(con, H2_E_PROTOCOL_ERROR); | |||
+ /* simplistic heuristic to discard additional DATA from recently-closed | |||
+ * streams (or half-closed (local)), where recently-closed here is | |||
+ * within 2-3 seconds of any (other) stream being half-closed (local) | |||
+ * or reset before that (other) stream received END_STREAM from peer. | |||
+ * (e.g. clients might fire off POST request followed by DATA, | |||
+ * and a response might be sent before processing DATA frames) | |||
+ * (id <= h2c->h2_cid) already checked above, else H2_E_PROTOCOL_ERROR | |||
+ * If the above conditions do not hold, then send GOAWAY to attempt to | |||
+ * reduce the chance of becoming an infinite data sink for misbehaving | |||
+ * clients, though remaining streams are still handled before the | |||
+ * connection is closed. */ | |||
chunkqueue_mark_written(cq, 9+len); | |||
- return 0; | |||
+ if (h2c->half_closed_ts + 2 >= log_epoch_secs) { | |||
+ h2_send_window_update(con, 0, len); /*(h2r->h2_rwin)*/ | |||
+ return 1; | |||
+ } | |||
+ else { | |||
+ if (!h2c->sent_goaway && 0 != alen) | |||
+ h2_send_goaway_e(con, H2_E_NO_ERROR); | |||
+ return 0; | |||
+ } | |||
} | |||
if (r->h2state == H2_STATE_CLOSED | |||
@@ -808,7 +831,7 @@ h2_recv_data (connection * const con, co | |||
} | |||
} | |||
/*(allow h2r->h2_rwin to dip below 0 so that entire frame is processed)*/ | |||
- /*(undeflow will not occur (with reasonable SETTINGS_MAX_FRAME_SIZE used) | |||
+ /*(underflow will not occur (with reasonable SETTINGS_MAX_FRAME_SIZE used) | |||
* since windows updated elsewhere and data is streamed to temp files if | |||
* not FDEVENT_STREAM_REQUEST_BUFMIN)*/ | |||
/*r->h2_rwin -= (int32_t)len;*/ | |||
@@ -2347,16 +2370,25 @@ h2_send_end_stream_data (request_st * co | |||
} }; | |||
dataframe.u[2] = htonl(r->h2id); | |||
- r->h2state = H2_STATE_CLOSED; | |||
/*(ignore window updates when sending 0-length DATA frame with END_STREAM)*/ | |||
chunkqueue_append_mem(con->write_queue, /*(+3 to skip over align pad)*/ | |||
(const char *)dataframe.c+3, sizeof(dataframe)-3); | |||
+ | |||
+ if (r->h2state != H2_STATE_HALF_CLOSED_REMOTE) { | |||
+ /* set timestamp for comparison; not tracking individual stream ids */ | |||
+ h2con * const h2c = con->h2; | |||
+ h2c->half_closed_ts = log_epoch_secs; | |||
+ /* indicate to peer that no more DATA should be sent from peer */ | |||
+ h2_send_rst_stream_id(r->h2id, con, H2_E_NO_ERROR); | |||
+ } | |||
+ r->h2state = H2_STATE_CLOSED; | |||
} | |||
void | |||
h2_send_end_stream (request_st * const r, connection * const con) | |||
{ | |||
+ if (r->h2state == H2_STATE_CLOSED) return; | |||
if (r->state != CON_STATE_ERROR && r->resp_body_finished) { | |||
/* CON_STATE_RESPONSE_END */ | |||
if (r->gw_dechunk && r->gw_dechunk->done | |||
--- a/src/h2.h | |||
+++ b/src/h2.h | |||
@@ -92,6 +92,7 @@ struct h2con { | |||
uint32_t s_max_header_list_size; /* SETTINGS_MAX_HEADER_LIST_SIZE */ | |||
struct lshpack_dec decoder; | |||
struct lshpack_enc encoder; | |||
+ time_t half_closed_ts; | |||
}; | |||
void h2_send_goaway (connection *con, request_h2error_t e); |