Fix libxml streams use wrong content-type
header when requesting a redirected resource CVE-2025-1219
Fix Stream HTTP wrapper header check might omit basic auth header CVE-2025-1736 Fix Stream HTTP wrapper truncate redirect location to 1024 bytes CVE-2025-1861 Fix Streams HTTP wrapper does not fail for headers without colon CVE-2025-1734 Fix Header parser of `http` stream wrapper does not handle folded headers CVE-2025-1217 Resolves: RHEL-87151
This commit is contained in:
parent
1099dd3ec7
commit
a52e0350ba
909
php-cve-2025-1217.patch
Normal file
909
php-cve-2025-1217.patch
Normal file
@ -0,0 +1,909 @@
|
||||
From 4fec08542748c25573063ffc53ea89cd5de1edf0 Mon Sep 17 00:00:00 2001
|
||||
From: Jakub Zelenka <bukka@php.net>
|
||||
Date: Tue, 31 Dec 2024 18:57:02 +0100
|
||||
Subject: [PATCH 01/11] Fix GHSA-ghsa-v8xr-gpvj-cx9g: http header folding
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This adds HTTP header folding support for HTTP wrapper response
|
||||
headers.
|
||||
|
||||
Reviewed-by: Tim Düsterhus <tim@tideways-gmbh.com>
|
||||
(cherry picked from commit d20b4c97a9f883b62b65b82d939c5af9a2028ef1)
|
||||
---
|
||||
ext/openssl/tests/ServerClientTestCase.inc | 65 +++-
|
||||
ext/standard/http_fopen_wrapper.c | 343 ++++++++++++------
|
||||
.../tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt | 49 +++
|
||||
.../tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt | 51 +++
|
||||
.../tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt | 49 +++
|
||||
.../tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt | 48 +++
|
||||
.../tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt | 48 +++
|
||||
.../tests/http/http_response_header_05.phpt | 30 --
|
||||
8 files changed, 534 insertions(+), 149 deletions(-)
|
||||
create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt
|
||||
delete mode 100644 ext/standard/tests/http/http_response_header_05.phpt
|
||||
|
||||
diff --git a/ext/openssl/tests/ServerClientTestCase.inc b/ext/openssl/tests/ServerClientTestCase.inc
|
||||
index 753366df6f4..61d45385b62 100644
|
||||
--- a/ext/openssl/tests/ServerClientTestCase.inc
|
||||
+++ b/ext/openssl/tests/ServerClientTestCase.inc
|
||||
@@ -4,14 +4,19 @@ const WORKER_ARGV_VALUE = 'RUN_WORKER';
|
||||
|
||||
const WORKER_DEFAULT_NAME = 'server';
|
||||
|
||||
-function phpt_notify($worker = WORKER_DEFAULT_NAME)
|
||||
+function phpt_notify(string $worker = WORKER_DEFAULT_NAME, string $message = ""): void
|
||||
{
|
||||
- ServerClientTestCase::getInstance()->notify($worker);
|
||||
+ ServerClientTestCase::getInstance()->notify($worker, $message);
|
||||
}
|
||||
|
||||
-function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null)
|
||||
+function phpt_wait($worker = WORKER_DEFAULT_NAME, $timeout = null): ?string
|
||||
{
|
||||
- ServerClientTestCase::getInstance()->wait($worker, $timeout);
|
||||
+ return ServerClientTestCase::getInstance()->wait($worker, $timeout);
|
||||
+}
|
||||
+
|
||||
+function phpt_notify_server_start($server): void
|
||||
+{
|
||||
+ ServerClientTestCase::getInstance()->notify_server_start($server);
|
||||
}
|
||||
|
||||
function phpt_has_sslv3() {
|
||||
@@ -119,43 +124,73 @@ class ServerClientTestCase
|
||||
eval($code);
|
||||
}
|
||||
|
||||
- public function run($masterCode, $workerCode)
|
||||
+ /**
|
||||
+ * Run client and all workers
|
||||
+ *
|
||||
+ * @param string $clientCode The client PHP code
|
||||
+ * @param string|array $workerCode
|
||||
+ * @param bool $ephemeral Select whether automatic port selection and automatic awaiting is used
|
||||
+ * @return void
|
||||
+ * @throws Exception
|
||||
+ */
|
||||
+ public function run(string $clientCode, string|array $workerCode, bool $ephemeral = true): void
|
||||
{
|
||||
if (!is_array($workerCode)) {
|
||||
$workerCode = [WORKER_DEFAULT_NAME => $workerCode];
|
||||
}
|
||||
- foreach ($workerCode as $worker => $code) {
|
||||
+ reset($workerCode);
|
||||
+ $code = current($workerCode);
|
||||
+ $worker = key($workerCode);
|
||||
+ while ($worker != null) {
|
||||
$this->spawnWorkerProcess($worker, $this->stripPhpTagsFromCode($code));
|
||||
+ $code = next($workerCode);
|
||||
+ if ($ephemeral) {
|
||||
+ $addr = trim($this->wait($worker));
|
||||
+ if (empty($addr)) {
|
||||
+ throw new \Exception("Failed server start");
|
||||
+ }
|
||||
+ if ($code === false) {
|
||||
+ $clientCode = preg_replace('/{{\s*ADDR\s*}}/', $addr, $clientCode);
|
||||
+ } else {
|
||||
+ $code = preg_replace('/{{\s*ADDR\s*}}/', $addr, $code);
|
||||
+ }
|
||||
+ }
|
||||
+ $worker = key($workerCode);
|
||||
}
|
||||
- eval($this->stripPhpTagsFromCode($masterCode));
|
||||
+
|
||||
+ eval($this->stripPhpTagsFromCode($clientCode));
|
||||
foreach ($workerCode as $worker => $code) {
|
||||
$this->cleanupWorkerProcess($worker);
|
||||
}
|
||||
}
|
||||
|
||||
- public function wait($worker, $timeout = null)
|
||||
+ public function wait($worker, $timeout = null): ?string
|
||||
{
|
||||
$handle = $this->isWorker ? STDIN : $this->workerStdOut[$worker];
|
||||
if ($timeout === null) {
|
||||
- fgets($handle);
|
||||
- return true;
|
||||
+ return fgets($handle);
|
||||
}
|
||||
|
||||
stream_set_blocking($handle, false);
|
||||
$read = [$handle];
|
||||
$result = stream_select($read, $write, $except, $timeout);
|
||||
if (!$result) {
|
||||
- return false;
|
||||
+ return null;
|
||||
}
|
||||
|
||||
- fgets($handle);
|
||||
+ $result = fgets($handle);
|
||||
stream_set_blocking($handle, true);
|
||||
- return true;
|
||||
+ return $result;
|
||||
+ }
|
||||
+
|
||||
+ public function notify(string $worker, string $message = ""): void
|
||||
+ {
|
||||
+ fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "$message\n");
|
||||
}
|
||||
|
||||
- public function notify($worker)
|
||||
+ public function notify_server_start($server): void
|
||||
{
|
||||
- fwrite($this->isWorker ? STDOUT : $this->workerStdIn[$worker], "\n");
|
||||
+ echo stream_socket_get_name($server, false) . "\n";
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
|
||||
index 40e6f3dd4c3..bfc88a74545 100644
|
||||
--- a/ext/standard/http_fopen_wrapper.c
|
||||
+++ b/ext/standard/http_fopen_wrapper.c
|
||||
@@ -114,6 +114,171 @@ static zend_bool check_has_header(const char *headers, const char *header) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+typedef struct _php_stream_http_response_header_info {
|
||||
+ php_stream_filter *transfer_encoding;
|
||||
+ size_t file_size;
|
||||
+ bool follow_location;
|
||||
+ char location[HTTP_HEADER_BLOCK_SIZE];
|
||||
+} php_stream_http_response_header_info;
|
||||
+
|
||||
+static void php_stream_http_response_header_info_init(
|
||||
+ php_stream_http_response_header_info *header_info)
|
||||
+{
|
||||
+ header_info->transfer_encoding = NULL;
|
||||
+ header_info->file_size = 0;
|
||||
+ header_info->follow_location = 1;
|
||||
+ header_info->location[0] = '\0';
|
||||
+}
|
||||
+
|
||||
+/* Trim white spaces from response header line and update its length */
|
||||
+static bool php_stream_http_response_header_trim(char *http_header_line,
|
||||
+ size_t *http_header_line_length)
|
||||
+{
|
||||
+ char *http_header_line_end = http_header_line + *http_header_line_length - 1;
|
||||
+ while (http_header_line_end >= http_header_line &&
|
||||
+ (*http_header_line_end == '\n' || *http_header_line_end == '\r')) {
|
||||
+ http_header_line_end--;
|
||||
+ }
|
||||
+
|
||||
+ /* The primary definition of an HTTP header in RFC 7230 states:
|
||||
+ * > Each header field consists of a case-insensitive field name followed
|
||||
+ * > by a colon (":"), optional leading whitespace, the field value, and
|
||||
+ * > optional trailing whitespace. */
|
||||
+
|
||||
+ /* Strip trailing whitespace */
|
||||
+ bool space_trim = (*http_header_line_end == ' ' || *http_header_line_end == '\t');
|
||||
+ if (space_trim) {
|
||||
+ do {
|
||||
+ http_header_line_end--;
|
||||
+ } while (http_header_line_end >= http_header_line &&
|
||||
+ (*http_header_line_end == ' ' || *http_header_line_end == '\t'));
|
||||
+ }
|
||||
+ http_header_line_end++;
|
||||
+ *http_header_line_end = '\0';
|
||||
+ *http_header_line_length = http_header_line_end - http_header_line;
|
||||
+
|
||||
+ return space_trim;
|
||||
+}
|
||||
+
|
||||
+/* Process folding headers of the current line and if there are none, parse last full response
|
||||
+ * header line. It returns NULL if the last header is finished, otherwise it returns updated
|
||||
+ * last header line. */
|
||||
+static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
|
||||
+ php_stream_context *context, int options, zend_string *last_header_line_str,
|
||||
+ char *header_line, size_t *header_line_length, int response_code,
|
||||
+ zval *response_header, php_stream_http_response_header_info *header_info)
|
||||
+{
|
||||
+ char *last_header_line = ZSTR_VAL(last_header_line_str);
|
||||
+ size_t last_header_line_length = ZSTR_LEN(last_header_line_str);
|
||||
+ char *last_header_line_end = ZSTR_VAL(last_header_line_str) + ZSTR_LEN(last_header_line_str) - 1;
|
||||
+
|
||||
+ /* Process non empty header line. */
|
||||
+ if (header_line && (*header_line != '\n' && *header_line != '\r')) {
|
||||
+ /* Removing trailing white spaces. */
|
||||
+ if (php_stream_http_response_header_trim(header_line, header_line_length) &&
|
||||
+ *header_line_length == 0) {
|
||||
+ /* Only spaces so treat as an empty folding header. */
|
||||
+ return last_header_line_str;
|
||||
+ }
|
||||
+
|
||||
+ /* Process folding headers if starting with a space or a tab. */
|
||||
+ if (header_line && (*header_line == ' ' || *header_line == '\t')) {
|
||||
+ char *http_folded_header_line = header_line;
|
||||
+ size_t http_folded_header_line_length = *header_line_length;
|
||||
+ /* Remove the leading white spaces. */
|
||||
+ while (*http_folded_header_line == ' ' || *http_folded_header_line == '\t') {
|
||||
+ http_folded_header_line++;
|
||||
+ http_folded_header_line_length--;
|
||||
+ }
|
||||
+ /* It has to have some characters because it would get returned after the call
|
||||
+ * php_stream_http_response_header_trim above. */
|
||||
+ ZEND_ASSERT(http_folded_header_line_length > 0);
|
||||
+ /* Concatenate last header line, space and current header line. */
|
||||
+ zend_string *extended_header_str = zend_string_concat3(
|
||||
+ last_header_line, last_header_line_length,
|
||||
+ " ", 1,
|
||||
+ http_folded_header_line, http_folded_header_line_length);
|
||||
+ zend_string_efree(last_header_line_str);
|
||||
+ last_header_line_str = extended_header_str;
|
||||
+ /* Return new header line. */
|
||||
+ return last_header_line_str;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* Find header separator position. */
|
||||
+ char *last_header_value = memchr(last_header_line, ':', last_header_line_length);
|
||||
+ if (last_header_value) {
|
||||
+ last_header_value++; /* Skip ':'. */
|
||||
+
|
||||
+ /* Strip leading whitespace. */
|
||||
+ while (last_header_value < last_header_line_end
|
||||
+ && (*last_header_value == ' ' || *last_header_value == '\t')) {
|
||||
+ last_header_value++;
|
||||
+ }
|
||||
+ } else {
|
||||
+ /* There is no colon. Set the value to the end of the header line, which is effectively
|
||||
+ * an empty string. */
|
||||
+ last_header_value = last_header_line_end;
|
||||
+ }
|
||||
+
|
||||
+ bool store_header = true;
|
||||
+ zval *tmpzval = NULL;
|
||||
+
|
||||
+ if (!strncasecmp(last_header_line, "Location:", sizeof("Location:")-1)) {
|
||||
+ /* Check if the location should be followed. */
|
||||
+ if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) {
|
||||
+ header_info->follow_location = zval_is_true(tmpzval);
|
||||
+ } else if (!((response_code >= 300 && response_code < 304)
|
||||
+ || 307 == response_code || 308 == response_code)) {
|
||||
+ /* The redirection should not be automatic if follow_location is not set and
|
||||
+ * response_code not in (300, 301, 302, 303 and 307)
|
||||
+ * see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1
|
||||
+ * RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */
|
||||
+ header_info->follow_location = 0;
|
||||
+ }
|
||||
+ strlcpy(header_info->location, last_header_value, sizeof(header_info->location));
|
||||
+ } else if (!strncasecmp(last_header_line, "Content-Type:", sizeof("Content-Type:")-1)) {
|
||||
+ php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, last_header_value, 0);
|
||||
+ } else if (!strncasecmp(last_header_line, "Content-Length:", sizeof("Content-Length:")-1)) {
|
||||
+ header_info->file_size = atoi(last_header_value);
|
||||
+ php_stream_notify_file_size(context, header_info->file_size, last_header_line, 0);
|
||||
+ } else if (
|
||||
+ !strncasecmp(last_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1)
|
||||
+ && !strncasecmp(last_header_value, "Chunked", sizeof("Chunked")-1)
|
||||
+ ) {
|
||||
+ /* Create filter to decode response body. */
|
||||
+ if (!(options & STREAM_ONLY_GET_HEADERS)) {
|
||||
+ zend_long decode = 1;
|
||||
+
|
||||
+ if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) {
|
||||
+ decode = zend_is_true(tmpzval);
|
||||
+ }
|
||||
+ if (decode) {
|
||||
+ if (header_info->transfer_encoding != NULL) {
|
||||
+ /* Prevent a memory leak in case there are more transfer-encoding headers. */
|
||||
+ php_stream_filter_free(header_info->transfer_encoding);
|
||||
+ }
|
||||
+ header_info->transfer_encoding = php_stream_filter_create(
|
||||
+ "dechunk", NULL, php_stream_is_persistent(stream));
|
||||
+ if (header_info->transfer_encoding != NULL) {
|
||||
+ /* Do not store transfer-encoding header. */
|
||||
+ store_header = false;
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (store_header) {
|
||||
+ zval http_header;
|
||||
+ ZVAL_NEW_STR(&http_header, last_header_line_str);
|
||||
+ zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header);
|
||||
+ } else {
|
||||
+ zend_string_efree(last_header_line_str);
|
||||
+ }
|
||||
+
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
|
||||
const char *path, const char *mode, int options, zend_string **opened_path,
|
||||
php_stream_context *context, int redirect_max, int flags,
|
||||
@@ -126,11 +291,12 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
|
||||
zend_string *tmp = NULL;
|
||||
char *ua_str = NULL;
|
||||
zval *ua_zval = NULL, *tmpzval = NULL, ssl_proxy_peer_name;
|
||||
- char location[HTTP_HEADER_BLOCK_SIZE];
|
||||
int reqok = 0;
|
||||
char *http_header_line = NULL;
|
||||
+ zend_string *last_header_line_str = NULL;
|
||||
+ php_stream_http_response_header_info header_info;
|
||||
char tmp_line[128];
|
||||
- size_t chunk_size = 0, file_size = 0;
|
||||
+ size_t chunk_size = 0;
|
||||
int eol_detect = 0;
|
||||
char *transport_string;
|
||||
zend_string *errstr = NULL;
|
||||
@@ -141,8 +307,6 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
|
||||
char *user_headers = NULL;
|
||||
int header_init = ((flags & HTTP_WRAPPER_HEADER_INIT) != 0);
|
||||
int redirected = ((flags & HTTP_WRAPPER_REDIRECTED) != 0);
|
||||
- zend_bool follow_location = 1;
|
||||
- php_stream_filter *transfer_encoding = NULL;
|
||||
int response_code;
|
||||
smart_str req_buf = {0};
|
||||
zend_bool custom_request_method;
|
||||
@@ -655,8 +819,6 @@ finish:
|
||||
/* send it */
|
||||
php_stream_write(stream, ZSTR_VAL(req_buf.s), ZSTR_LEN(req_buf.s));
|
||||
|
||||
- location[0] = '\0';
|
||||
-
|
||||
if (Z_ISUNDEF_P(response_header)) {
|
||||
array_init(response_header);
|
||||
}
|
||||
@@ -738,130 +900,101 @@ finish:
|
||||
}
|
||||
}
|
||||
|
||||
- /* read past HTTP headers */
|
||||
+ php_stream_http_response_header_info_init(&header_info);
|
||||
|
||||
+ /* read past HTTP headers */
|
||||
while (!php_stream_eof(stream)) {
|
||||
size_t http_header_line_length;
|
||||
|
||||
if (http_header_line != NULL) {
|
||||
efree(http_header_line);
|
||||
}
|
||||
- if ((http_header_line = php_stream_get_line(stream, NULL, 0, &http_header_line_length)) && *http_header_line != '\n' && *http_header_line != '\r') {
|
||||
- char *e = http_header_line + http_header_line_length - 1;
|
||||
- char *http_header_value;
|
||||
-
|
||||
- while (e >= http_header_line && (*e == '\n' || *e == '\r')) {
|
||||
- e--;
|
||||
- }
|
||||
-
|
||||
- /* The primary definition of an HTTP header in RFC 7230 states:
|
||||
- * > Each header field consists of a case-insensitive field name followed
|
||||
- * > by a colon (":"), optional leading whitespace, the field value, and
|
||||
- * > optional trailing whitespace. */
|
||||
-
|
||||
- /* Strip trailing whitespace */
|
||||
- while (e >= http_header_line && (*e == ' ' || *e == '\t')) {
|
||||
- e--;
|
||||
- }
|
||||
-
|
||||
- /* Terminate header line */
|
||||
- e++;
|
||||
- *e = '\0';
|
||||
- http_header_line_length = e - http_header_line;
|
||||
-
|
||||
- http_header_value = memchr(http_header_line, ':', http_header_line_length);
|
||||
- if (http_header_value) {
|
||||
- http_header_value++; /* Skip ':' */
|
||||
-
|
||||
- /* Strip leading whitespace */
|
||||
- while (http_header_value < e
|
||||
- && (*http_header_value == ' ' || *http_header_value == '\t')) {
|
||||
- http_header_value++;
|
||||
+ if ((http_header_line = php_stream_get_line(stream, NULL, 0, &http_header_line_length))) {
|
||||
+ bool last_line;
|
||||
+ if (*http_header_line == '\r') {
|
||||
+ if (http_header_line[1] != '\n') {
|
||||
+ php_stream_close(stream);
|
||||
+ stream = NULL;
|
||||
+ php_stream_wrapper_log_error(wrapper, options,
|
||||
+ "HTTP invalid header name (cannot start with CR character)!");
|
||||
+ goto out;
|
||||
}
|
||||
+ last_line = true;
|
||||
+ } else if (*http_header_line == '\n') {
|
||||
+ last_line = true;
|
||||
} else {
|
||||
- /* There is no colon. Set the value to the end of the header line, which is
|
||||
- * effectively an empty string. */
|
||||
- http_header_value = e;
|
||||
+ last_line = false;
|
||||
}
|
||||
-
|
||||
- if (!strncasecmp(http_header_line, "Location:", sizeof("Location:")-1)) {
|
||||
- if (context && (tmpzval = php_stream_context_get_option(context, "http", "follow_location")) != NULL) {
|
||||
- follow_location = zval_is_true(tmpzval);
|
||||
- } else if (!((response_code >= 300 && response_code < 304)
|
||||
- || 307 == response_code || 308 == response_code)) {
|
||||
- /* we shouldn't redirect automatically
|
||||
- if follow_location isn't set and response_code not in (300, 301, 302, 303 and 307)
|
||||
- see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.1
|
||||
- RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */
|
||||
- follow_location = 0;
|
||||
+
|
||||
+ if (last_header_line_str != NULL) {
|
||||
+ /* Parse last header line. */
|
||||
+ last_header_line_str = php_stream_http_response_headers_parse(stream, context,
|
||||
+ options, last_header_line_str, http_header_line, &http_header_line_length,
|
||||
+ response_code, response_header, &header_info);
|
||||
+ if (last_header_line_str != NULL) {
|
||||
+ /* Folding header present so continue. */
|
||||
+ continue;
|
||||
}
|
||||
- strlcpy(location, http_header_value, sizeof(location));
|
||||
- } else if (!strncasecmp(http_header_line, "Content-Type:", sizeof("Content-Type:")-1)) {
|
||||
- php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, http_header_value, 0);
|
||||
- } else if (!strncasecmp(http_header_line, "Content-Length:", sizeof("Content-Length:")-1)) {
|
||||
- file_size = atoi(http_header_value);
|
||||
- php_stream_notify_file_size(context, file_size, http_header_line, 0);
|
||||
- } else if (
|
||||
- !strncasecmp(http_header_line, "Transfer-Encoding:", sizeof("Transfer-Encoding:")-1)
|
||||
- && !strncasecmp(http_header_value, "Chunked", sizeof("Chunked")-1)
|
||||
- ) {
|
||||
-
|
||||
- /* create filter to decode response body */
|
||||
- if (!(options & STREAM_ONLY_GET_HEADERS)) {
|
||||
- zend_long decode = 1;
|
||||
-
|
||||
- if (context && (tmpzval = php_stream_context_get_option(context, "http", "auto_decode")) != NULL) {
|
||||
- decode = zend_is_true(tmpzval);
|
||||
- }
|
||||
- if (decode) {
|
||||
- transfer_encoding = php_stream_filter_create("dechunk", NULL, php_stream_is_persistent(stream));
|
||||
- if (transfer_encoding) {
|
||||
- /* don't store transfer-encodeing header */
|
||||
- continue;
|
||||
- }
|
||||
- }
|
||||
+ } else if (!last_line) {
|
||||
+ /* The first line cannot start with spaces. */
|
||||
+ if (*http_header_line == ' ' || *http_header_line == '\t') {
|
||||
+ php_stream_close(stream);
|
||||
+ stream = NULL;
|
||||
+ php_stream_wrapper_log_error(wrapper, options,
|
||||
+ "HTTP invalid response format (folding header at the start)!");
|
||||
+ goto out;
|
||||
}
|
||||
+ /* Trim the first line if it is not the last line. */
|
||||
+ php_stream_http_response_header_trim(http_header_line, &http_header_line_length);
|
||||
}
|
||||
-
|
||||
- {
|
||||
- zval http_header;
|
||||
- ZVAL_STRINGL(&http_header, http_header_line, http_header_line_length);
|
||||
- zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header);
|
||||
+ if (last_line) {
|
||||
+ /* For the last line the last header line must be NULL. */
|
||||
+ ZEND_ASSERT(last_header_line_str == NULL);
|
||||
+ break;
|
||||
}
|
||||
+ /* Save current line as the last line so it gets parsed in the next round. */
|
||||
+ last_header_line_str = zend_string_init(http_header_line, http_header_line_length, 0);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- if (!reqok || (location[0] != '\0' && follow_location)) {
|
||||
- if (!follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) {
|
||||
+ /* If the stream was closed early, we still want to process the last line to keep BC. */
|
||||
+ if (last_header_line_str != NULL) {
|
||||
+ php_stream_http_response_headers_parse(stream, context, options, last_header_line_str,
|
||||
+ NULL, NULL, response_code, response_header, &header_info);
|
||||
+ }
|
||||
+
|
||||
+ if (!reqok || (header_info.location[0] != '\0' && header_info.follow_location)) {
|
||||
+ if (!header_info.follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
- if (location[0] != '\0')
|
||||
- php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, location, 0);
|
||||
+ if (header_info.location[0] != '\0')
|
||||
+ php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, header_info.location, 0);
|
||||
|
||||
php_stream_close(stream);
|
||||
stream = NULL;
|
||||
|
||||
- if (transfer_encoding) {
|
||||
- php_stream_filter_free(transfer_encoding);
|
||||
- transfer_encoding = NULL;
|
||||
+ if (header_info.transfer_encoding) {
|
||||
+ php_stream_filter_free(header_info.transfer_encoding);
|
||||
+ header_info.transfer_encoding = NULL;
|
||||
}
|
||||
|
||||
- if (location[0] != '\0') {
|
||||
+ if (header_info.location[0] != '\0') {
|
||||
|
||||
char new_path[HTTP_HEADER_BLOCK_SIZE];
|
||||
char loc_path[HTTP_HEADER_BLOCK_SIZE];
|
||||
|
||||
*new_path='\0';
|
||||
- if (strlen(location)<8 || (strncasecmp(location, "http://", sizeof("http://")-1) &&
|
||||
- strncasecmp(location, "https://", sizeof("https://")-1) &&
|
||||
- strncasecmp(location, "ftp://", sizeof("ftp://")-1) &&
|
||||
- strncasecmp(location, "ftps://", sizeof("ftps://")-1)))
|
||||
+ if (strlen(header_info.location) < 8 ||
|
||||
+ (strncasecmp(header_info.location, "http://", sizeof("http://")-1) &&
|
||||
+ strncasecmp(header_info.location, "https://", sizeof("https://")-1) &&
|
||||
+ strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) &&
|
||||
+ strncasecmp(header_info.location, "ftps://", sizeof("ftps://")-1)))
|
||||
{
|
||||
- if (*location != '/') {
|
||||
- if (*(location+1) != '\0' && resource->path) {
|
||||
+ if (*header_info.location != '/') {
|
||||
+ if (*(header_info.location+1) != '\0' && resource->path) {
|
||||
char *s = strrchr(ZSTR_VAL(resource->path), '/');
|
||||
if (!s) {
|
||||
s = ZSTR_VAL(resource->path);
|
||||
@@ -877,15 +1010,17 @@ finish:
|
||||
if (resource->path &&
|
||||
ZSTR_VAL(resource->path)[0] == '/' &&
|
||||
ZSTR_VAL(resource->path)[1] == '\0') {
|
||||
- snprintf(loc_path, sizeof(loc_path) - 1, "%s%s", ZSTR_VAL(resource->path), location);
|
||||
+ snprintf(loc_path, sizeof(loc_path) - 1, "%s%s",
|
||||
+ ZSTR_VAL(resource->path), header_info.location);
|
||||
} else {
|
||||
- snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s", ZSTR_VAL(resource->path), location);
|
||||
+ snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s",
|
||||
+ ZSTR_VAL(resource->path), header_info.location);
|
||||
}
|
||||
} else {
|
||||
- snprintf(loc_path, sizeof(loc_path) - 1, "/%s", location);
|
||||
+ snprintf(loc_path, sizeof(loc_path) - 1, "/%s", header_info.location);
|
||||
}
|
||||
} else {
|
||||
- strlcpy(loc_path, location, sizeof(loc_path));
|
||||
+ strlcpy(loc_path, header_info.location, sizeof(loc_path));
|
||||
}
|
||||
if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) {
|
||||
snprintf(new_path, sizeof(new_path) - 1, "%s://%s:%d%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), resource->port, loc_path);
|
||||
@@ -893,7 +1028,7 @@ finish:
|
||||
snprintf(new_path, sizeof(new_path) - 1, "%s://%s%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), loc_path);
|
||||
}
|
||||
} else {
|
||||
- strlcpy(new_path, location, sizeof(new_path));
|
||||
+ strlcpy(new_path, header_info.location, sizeof(new_path));
|
||||
}
|
||||
|
||||
php_url_free(resource);
|
||||
@@ -946,7 +1081,7 @@ out:
|
||||
if (header_init) {
|
||||
ZVAL_COPY(&stream->wrapperdata, response_header);
|
||||
}
|
||||
- php_stream_notify_progress_init(context, 0, file_size);
|
||||
+ php_stream_notify_progress_init(context, 0, header_info.file_size);
|
||||
|
||||
/* Restore original chunk size now that we're done with headers */
|
||||
if (options & STREAM_WILL_CAST)
|
||||
@@ -962,8 +1097,8 @@ out:
|
||||
/* restore mode */
|
||||
strlcpy(stream->mode, mode, sizeof(stream->mode));
|
||||
|
||||
- if (transfer_encoding) {
|
||||
- php_stream_filter_append(&stream->readfilters, transfer_encoding);
|
||||
+ if (header_info.transfer_encoding) {
|
||||
+ php_stream_filter_append(&stream->readfilters, header_info.transfer_encoding);
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..f935b5a02ca
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-001.phpt
|
||||
@@ -0,0 +1,49 @@
|
||||
+--TEST--
|
||||
+GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (single)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\n charset=utf-8\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+Found the mime-type: text/html; charset=utf-8
|
||||
+string(4) "body"
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(38) "Content-Type: text/html; charset=utf-8"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..078d605b671
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-002.phpt
|
||||
@@ -0,0 +1,51 @@
|
||||
+--TEST--
|
||||
+GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (multiple)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\nCustom-Header: somevalue;\r\n param1=value1; \r\n param2=value2\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+Found the mime-type: text/html;
|
||||
+string(4) "body"
|
||||
+array(3) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(24) "Content-Type: text/html;"
|
||||
+ [2]=>
|
||||
+ string(54) "Custom-Header: somevalue; param1=value1; param2=value2"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..ad5ddc879ce
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-003.phpt
|
||||
@@ -0,0 +1,49 @@
|
||||
+--TEST--
|
||||
+GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (empty)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html;\r\n \r\n charset=utf-8\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+Found the mime-type: text/html; charset=utf-8
|
||||
+string(4) "body"
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(38) "Content-Type: text/html; charset=utf-8"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..d0396e819fb
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-004.phpt
|
||||
@@ -0,0 +1,48 @@
|
||||
+--TEST--
|
||||
+GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (first line)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\n Content-Type: text/html;\r\n \r\n charset=utf-8\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+
|
||||
+Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid response format (folding header at the start)! in %s
|
||||
+bool(false)
|
||||
+array(1) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..037d2002cc5
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-v8xr-gpvj-cx9g-005.phpt
|
||||
@@ -0,0 +1,48 @@
|
||||
+--TEST--
|
||||
+GHSA-v8xr-gpvj-cx9g: Header parser of http stream wrapper does not handle folded headers (CR before header name)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\n\rIgnored: ignored\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+
|
||||
+Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid header name (cannot start with CR character)! in %s
|
||||
+bool(false)
|
||||
+array(1) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/http_response_header_05.phpt b/ext/standard/tests/http/http_response_header_05.phpt
|
||||
deleted file mode 100644
|
||||
index c5fe60fa612..00000000000
|
||||
--- a/ext/standard/tests/http/http_response_header_05.phpt
|
||||
+++ /dev/null
|
||||
@@ -1,30 +0,0 @@
|
||||
---TEST--
|
||||
-$http_reponse_header (whitespace-only "header")
|
||||
---SKIPIF--
|
||||
-<?php require 'server.inc'; http_server_skipif(); ?>
|
||||
---INI--
|
||||
-allow_url_fopen=1
|
||||
---FILE--
|
||||
-<?php
|
||||
-require 'server.inc';
|
||||
-
|
||||
-$responses = array(
|
||||
- "data://text/plain,HTTP/1.0 200 Ok\r\n \r\n\r\nBody",
|
||||
-);
|
||||
-
|
||||
-['pid' => $pid, 'uri' => $uri] = http_server($responses, $output);
|
||||
-
|
||||
-$f = file_get_contents($uri);
|
||||
-var_dump($f);
|
||||
-var_dump($http_response_header);
|
||||
-
|
||||
-http_server_kill($pid);
|
||||
-
|
||||
---EXPECT--
|
||||
-string(4) "Body"
|
||||
-array(2) {
|
||||
- [0]=>
|
||||
- string(15) "HTTP/1.0 200 Ok"
|
||||
- [1]=>
|
||||
- string(0) ""
|
||||
-}
|
||||
--
|
||||
2.48.1
|
||||
|
1779
php-cve-2025-1219.patch
Normal file
1779
php-cve-2025-1219.patch
Normal file
File diff suppressed because it is too large
Load Diff
300
php-cve-2025-1734.patch
Normal file
300
php-cve-2025-1734.patch
Normal file
@ -0,0 +1,300 @@
|
||||
From e81d0cd14bfeb17e899c73e3aece4991bbda76af Mon Sep 17 00:00:00 2001
|
||||
From: Jakub Zelenka <bukka@php.net>
|
||||
Date: Sun, 19 Jan 2025 17:49:53 +0100
|
||||
Subject: [PATCH 02/11] Fix GHSA-pcmh-g36c-qc44: http headers without colon
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The header line must contain colon otherwise it is invalid and it needs
|
||||
to fail.
|
||||
|
||||
Reviewed-by: Tim Düsterhus <tim@tideways-gmbh.com>
|
||||
(cherry picked from commit 0548c4c1756724a89ef8310709419b08aadb2b3b)
|
||||
---
|
||||
ext/standard/http_fopen_wrapper.c | 51 ++++++++++++++-----
|
||||
ext/standard/tests/http/bug47021.phpt | 22 ++++----
|
||||
ext/standard/tests/http/bug75535.phpt | 4 +-
|
||||
.../tests/http/ghsa-pcmh-g36c-qc44-001.phpt | 51 +++++++++++++++++++
|
||||
.../tests/http/ghsa-pcmh-g36c-qc44-002.phpt | 51 +++++++++++++++++++
|
||||
5 files changed, 154 insertions(+), 25 deletions(-)
|
||||
create mode 100644 ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt
|
||||
|
||||
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
|
||||
index bfc88a74545..7ee22b85f88 100644
|
||||
--- a/ext/standard/http_fopen_wrapper.c
|
||||
+++ b/ext/standard/http_fopen_wrapper.c
|
||||
@@ -117,6 +117,7 @@ static zend_bool check_has_header(const char *headers, const char *header) {
|
||||
typedef struct _php_stream_http_response_header_info {
|
||||
php_stream_filter *transfer_encoding;
|
||||
size_t file_size;
|
||||
+ bool error;
|
||||
bool follow_location;
|
||||
char location[HTTP_HEADER_BLOCK_SIZE];
|
||||
} php_stream_http_response_header_info;
|
||||
@@ -126,6 +127,7 @@ static void php_stream_http_response_header_info_init(
|
||||
{
|
||||
header_info->transfer_encoding = NULL;
|
||||
header_info->file_size = 0;
|
||||
+ header_info->error = false;
|
||||
header_info->follow_location = 1;
|
||||
header_info->location[0] = '\0';
|
||||
}
|
||||
@@ -163,10 +165,11 @@ static bool php_stream_http_response_header_trim(char *http_header_line,
|
||||
/* Process folding headers of the current line and if there are none, parse last full response
|
||||
* header line. It returns NULL if the last header is finished, otherwise it returns updated
|
||||
* last header line. */
|
||||
-static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
|
||||
- php_stream_context *context, int options, zend_string *last_header_line_str,
|
||||
- char *header_line, size_t *header_line_length, int response_code,
|
||||
- zval *response_header, php_stream_http_response_header_info *header_info)
|
||||
+static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *wrapper,
|
||||
+ php_stream *stream, php_stream_context *context, int options,
|
||||
+ zend_string *last_header_line_str, char *header_line, size_t *header_line_length,
|
||||
+ int response_code, zval *response_header,
|
||||
+ php_stream_http_response_header_info *header_info)
|
||||
{
|
||||
char *last_header_line = ZSTR_VAL(last_header_line_str);
|
||||
size_t last_header_line_length = ZSTR_LEN(last_header_line_str);
|
||||
@@ -208,6 +211,19 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
|
||||
/* Find header separator position. */
|
||||
char *last_header_value = memchr(last_header_line, ':', last_header_line_length);
|
||||
if (last_header_value) {
|
||||
+ /* Verify there is no space in header name */
|
||||
+ char *last_header_name = last_header_line + 1;
|
||||
+ while (last_header_name < last_header_value) {
|
||||
+ if (*last_header_name == ' ' || *last_header_name == '\t') {
|
||||
+ header_info->error = true;
|
||||
+ php_stream_wrapper_log_error(wrapper, options,
|
||||
+ "HTTP invalid response format (space in header name)!");
|
||||
+ zend_string_efree(last_header_line_str);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ ++last_header_name;
|
||||
+ }
|
||||
+
|
||||
last_header_value++; /* Skip ':'. */
|
||||
|
||||
/* Strip leading whitespace. */
|
||||
@@ -216,9 +232,12 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
|
||||
last_header_value++;
|
||||
}
|
||||
} else {
|
||||
- /* There is no colon. Set the value to the end of the header line, which is effectively
|
||||
- * an empty string. */
|
||||
- last_header_value = last_header_line_end;
|
||||
+ /* There is no colon which means invalid response so error. */
|
||||
+ header_info->error = true;
|
||||
+ php_stream_wrapper_log_error(wrapper, options,
|
||||
+ "HTTP invalid response format (no colon in header line)!");
|
||||
+ zend_string_efree(last_header_line_str);
|
||||
+ return NULL;
|
||||
}
|
||||
|
||||
bool store_header = true;
|
||||
@@ -928,10 +947,16 @@ finish:
|
||||
|
||||
if (last_header_line_str != NULL) {
|
||||
/* Parse last header line. */
|
||||
- last_header_line_str = php_stream_http_response_headers_parse(stream, context,
|
||||
- options, last_header_line_str, http_header_line, &http_header_line_length,
|
||||
- response_code, response_header, &header_info);
|
||||
- if (last_header_line_str != NULL) {
|
||||
+ last_header_line_str = php_stream_http_response_headers_parse(wrapper, stream,
|
||||
+ context, options, last_header_line_str, http_header_line,
|
||||
+ &http_header_line_length, response_code, response_header, &header_info);
|
||||
+ if (EXPECTED(last_header_line_str == NULL)) {
|
||||
+ if (UNEXPECTED(header_info.error)) {
|
||||
+ php_stream_close(stream);
|
||||
+ stream = NULL;
|
||||
+ goto out;
|
||||
+ }
|
||||
+ } else {
|
||||
/* Folding header present so continue. */
|
||||
continue;
|
||||
}
|
||||
@@ -961,8 +986,8 @@ finish:
|
||||
|
||||
/* If the stream was closed early, we still want to process the last line to keep BC. */
|
||||
if (last_header_line_str != NULL) {
|
||||
- php_stream_http_response_headers_parse(stream, context, options, last_header_line_str,
|
||||
- NULL, NULL, response_code, response_header, &header_info);
|
||||
+ php_stream_http_response_headers_parse(wrapper, stream, context, options,
|
||||
+ last_header_line_str, NULL, NULL, response_code, response_header, &header_info);
|
||||
}
|
||||
|
||||
if (!reqok || (header_info.location[0] != '\0' && header_info.follow_location)) {
|
||||
diff --git a/ext/standard/tests/http/bug47021.phpt b/ext/standard/tests/http/bug47021.phpt
|
||||
index 326eceb687a..168721f4ec1 100644
|
||||
--- a/ext/standard/tests/http/bug47021.phpt
|
||||
+++ b/ext/standard/tests/http/bug47021.phpt
|
||||
@@ -70,23 +70,27 @@ do_test(1, true);
|
||||
echo "\n";
|
||||
|
||||
?>
|
||||
---EXPECT--
|
||||
+--EXPECTF--
|
||||
+
|
||||
Type='text/plain'
|
||||
Hello
|
||||
-Size=5
|
||||
-World
|
||||
+
|
||||
+Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
+
|
||||
|
||||
Type='text/plain'
|
||||
Hello
|
||||
-Size=5
|
||||
-World
|
||||
+
|
||||
+Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
+
|
||||
|
||||
Type='text/plain'
|
||||
Hello
|
||||
-Size=5
|
||||
-World
|
||||
+
|
||||
+Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
+
|
||||
|
||||
Type='text/plain'
|
||||
Hello
|
||||
-Size=5
|
||||
-World
|
||||
+
|
||||
+Warning: file_get_contents(http://%s:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
diff --git a/ext/standard/tests/http/bug75535.phpt b/ext/standard/tests/http/bug75535.phpt
|
||||
index 7b015890d2f..94348d1a027 100644
|
||||
--- a/ext/standard/tests/http/bug75535.phpt
|
||||
+++ b/ext/standard/tests/http/bug75535.phpt
|
||||
@@ -21,9 +21,7 @@ http_server_kill($pid);
|
||||
|
||||
--EXPECT--
|
||||
string(0) ""
|
||||
-array(2) {
|
||||
+array(1) {
|
||||
[0]=>
|
||||
string(15) "HTTP/1.0 200 Ok"
|
||||
- [1]=>
|
||||
- string(14) "Content-Length"
|
||||
}
|
||||
diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..bb7945ce62d
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-001.phpt
|
||||
@@ -0,0 +1,51 @@
|
||||
+--TEST--
|
||||
+GHSA-pcmh-g36c-qc44: Header parser of http stream wrapper does not verify header name and colon (colon)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\nWrong-Header\r\nGood-Header: test\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+Found the mime-type: text/html
|
||||
+
|
||||
+Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid response format (no colon in header line)! in %s
|
||||
+bool(false)
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(23) "Content-Type: text/html"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..1d0e4fa70a2
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-pcmh-g36c-qc44-002.phpt
|
||||
@@ -0,0 +1,51 @@
|
||||
+--TEST--
|
||||
+GHSA-pcmh-g36c-qc44: Header parser of http stream wrapper does not verify header name and colon (name)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html\r\nWrong-Header : test\r\nGood-Header: test\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(file_get_contents("http://{{ ADDR }}", false, $ctx));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+Found the mime-type: text/html
|
||||
+
|
||||
+Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP invalid response format (space in header name)! in %s
|
||||
+bool(false)
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(23) "Content-Type: text/html"
|
||||
+}
|
||||
--
|
||||
2.48.1
|
||||
|
241
php-cve-2025-1736.patch
Normal file
241
php-cve-2025-1736.patch
Normal file
@ -0,0 +1,241 @@
|
||||
From 8f65ef50929f6781f4973325f9b619f02cce19d8 Mon Sep 17 00:00:00 2001
|
||||
From: Jakub Zelenka <bukka@php.net>
|
||||
Date: Fri, 14 Feb 2025 19:17:22 +0100
|
||||
Subject: [PATCH 04/11] Fix GHSA-hgf5-96fm-v528: http user header check of crlf
|
||||
|
||||
(cherry picked from commit 41d49abbd99dab06cdae4834db664435f8177174)
|
||||
---
|
||||
ext/standard/http_fopen_wrapper.c | 2 +-
|
||||
.../tests/http/ghsa-hgf5-96fm-v528-001.phpt | 65 +++++++++++++++++++
|
||||
.../tests/http/ghsa-hgf5-96fm-v528-002.phpt | 62 ++++++++++++++++++
|
||||
.../tests/http/ghsa-hgf5-96fm-v528-003.phpt | 64 ++++++++++++++++++
|
||||
4 files changed, 192 insertions(+), 1 deletion(-)
|
||||
create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
|
||||
|
||||
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
|
||||
index e9b2486a7c9..64703c2f56b 100644
|
||||
--- a/ext/standard/http_fopen_wrapper.c
|
||||
+++ b/ext/standard/http_fopen_wrapper.c
|
||||
@@ -107,7 +107,7 @@ static inline void strip_header(char *header_bag, char *lc_header_bag,
|
||||
static zend_bool check_has_header(const char *headers, const char *header) {
|
||||
const char *s = headers;
|
||||
while ((s = strstr(s, header))) {
|
||||
- if (s == headers || *(s-1) == '\n') {
|
||||
+ if (s == headers || (*(s-1) == '\n' && *(s-2) == '\r')) {
|
||||
return 1;
|
||||
}
|
||||
s++;
|
||||
diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..c40123560ef
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-001.phpt
|
||||
@@ -0,0 +1,65 @@
|
||||
+--TEST--
|
||||
+GHSA-hgf5-96fm-v528: Stream HTTP wrapper header check might omit basic auth header (incorrect inside pos)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ $result = fread($conn, 1024);
|
||||
+ $encoded_result = base64_encode($result);
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
||||
+
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ $opts = [
|
||||
+ "http" => [
|
||||
+ "method" => "GET",
|
||||
+ "header" => "Cookie: foo=bar\nauthorization:x\r\n"
|
||||
+ ]
|
||||
+ ];
|
||||
+ $ctx = stream_context_create($opts);
|
||||
+ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+array(7) {
|
||||
+ [0]=>
|
||||
+ string(14) "GET / HTTP/1.1"
|
||||
+ [1]=>
|
||||
+ string(33) "Authorization: Basic dXNlcjpwd2Q="
|
||||
+ [2]=>
|
||||
+ string(21) "Host: 127.0.0.1:%d"
|
||||
+ [3]=>
|
||||
+ string(17) "Connection: close"
|
||||
+ [4]=>
|
||||
+ string(31) "Cookie: foo=bar
|
||||
+authorization:x"
|
||||
+ [5]=>
|
||||
+ string(0) ""
|
||||
+ [6]=>
|
||||
+ string(0) ""
|
||||
+}
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(38) "Content-Type: text/html; charset=utf-8"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..37a47df060a
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-002.phpt
|
||||
@@ -0,0 +1,62 @@
|
||||
+--TEST--
|
||||
+GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct start pos)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ $result = fread($conn, 1024);
|
||||
+ $encoded_result = base64_encode($result);
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
||||
+
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ $opts = [
|
||||
+ "http" => [
|
||||
+ "method" => "GET",
|
||||
+ "header" => "Authorization: Bearer x\r\n"
|
||||
+ ]
|
||||
+ ];
|
||||
+ $ctx = stream_context_create($opts);
|
||||
+ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+array(6) {
|
||||
+ [0]=>
|
||||
+ string(14) "GET / HTTP/1.1"
|
||||
+ [1]=>
|
||||
+ string(21) "Host: 127.0.0.1:%d"
|
||||
+ [2]=>
|
||||
+ string(17) "Connection: close"
|
||||
+ [3]=>
|
||||
+ string(23) "Authorization: Bearer x"
|
||||
+ [4]=>
|
||||
+ string(0) ""
|
||||
+ [5]=>
|
||||
+ string(0) ""
|
||||
+}
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(38) "Content-Type: text/html; charset=utf-8"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..6c84679ff63
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-hgf5-96fm-v528-003.phpt
|
||||
@@ -0,0 +1,64 @@
|
||||
+--TEST--
|
||||
+GHSA-hgf5-96fm-v528: Header parser of http stream wrapper does not handle folded headers (correct middle pos)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+ $ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ $result = fread($conn, 1024);
|
||||
+ $encoded_result = base64_encode($result);
|
||||
+
|
||||
+ fwrite($conn, "HTTP/1.0 200 Ok\r\nContent-Type: text/html; charset=utf-8\r\n\r\n$encoded_result\r\n");
|
||||
+
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ $opts = [
|
||||
+ "http" => [
|
||||
+ "method" => "GET",
|
||||
+ "header" => "Cookie: x=y\r\nAuthorization: Bearer x\r\n"
|
||||
+ ]
|
||||
+ ];
|
||||
+ $ctx = stream_context_create($opts);
|
||||
+ var_dump(explode("\r\n", base64_decode(file_get_contents("http://user:pwd@{{ ADDR }}", false, $ctx))));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+array(7) {
|
||||
+ [0]=>
|
||||
+ string(14) "GET / HTTP/1.1"
|
||||
+ [1]=>
|
||||
+ string(21) "Host: 127.0.0.1:%d"
|
||||
+ [2]=>
|
||||
+ string(17) "Connection: close"
|
||||
+ [3]=>
|
||||
+ string(11) "Cookie: x=y"
|
||||
+ [4]=>
|
||||
+ string(23) "Authorization: Bearer x"
|
||||
+ [5]=>
|
||||
+ string(0) ""
|
||||
+ [6]=>
|
||||
+ string(0) ""
|
||||
+}
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 200 Ok"
|
||||
+ [1]=>
|
||||
+ string(38) "Content-Type: text/html; charset=utf-8"
|
||||
+}
|
||||
--
|
||||
2.48.1
|
||||
|
348
php-cve-2025-1861.patch
Normal file
348
php-cve-2025-1861.patch
Normal file
@ -0,0 +1,348 @@
|
||||
From adc7e9f20c9a9aab9cd23ca47ec3fb96287898ae Mon Sep 17 00:00:00 2001
|
||||
From: Jakub Zelenka <bukka@php.net>
|
||||
Date: Tue, 4 Mar 2025 09:01:34 +0100
|
||||
Subject: [PATCH 03/11] Fix GHSA-52jp-hrpf-2jff: http redirect location
|
||||
truncation
|
||||
|
||||
It converts the allocation of location to be on heap instead of stack
|
||||
and errors if the location length is greater than 8086 bytes.
|
||||
|
||||
(cherry picked from commit ac1a054bb3eb5994a199e8b18cca28cbabf5943e)
|
||||
---
|
||||
ext/standard/http_fopen_wrapper.c | 87 ++++++++++++-------
|
||||
.../tests/http/ghsa-52jp-hrpf-2jff-001.phpt | 58 +++++++++++++
|
||||
.../tests/http/ghsa-52jp-hrpf-2jff-002.phpt | 55 ++++++++++++
|
||||
3 files changed, 168 insertions(+), 32 deletions(-)
|
||||
create mode 100644 ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt
|
||||
create mode 100644 ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt
|
||||
|
||||
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
|
||||
index 7ee22b85f88..e9b2486a7c9 100644
|
||||
--- a/ext/standard/http_fopen_wrapper.c
|
||||
+++ b/ext/standard/http_fopen_wrapper.c
|
||||
@@ -67,15 +67,16 @@
|
||||
|
||||
#include "php_fopen_wrappers.h"
|
||||
|
||||
-#define HTTP_HEADER_BLOCK_SIZE 1024
|
||||
-#define PHP_URL_REDIRECT_MAX 20
|
||||
-#define HTTP_HEADER_USER_AGENT 1
|
||||
-#define HTTP_HEADER_HOST 2
|
||||
-#define HTTP_HEADER_AUTH 4
|
||||
-#define HTTP_HEADER_FROM 8
|
||||
-#define HTTP_HEADER_CONTENT_LENGTH 16
|
||||
-#define HTTP_HEADER_TYPE 32
|
||||
-#define HTTP_HEADER_CONNECTION 64
|
||||
+#define HTTP_HEADER_BLOCK_SIZE 1024
|
||||
+#define HTTP_HEADER_MAX_LOCATION_SIZE 8182 /* 8192 - 10 (size of "Location: ") */
|
||||
+#define PHP_URL_REDIRECT_MAX 20
|
||||
+#define HTTP_HEADER_USER_AGENT 1
|
||||
+#define HTTP_HEADER_HOST 2
|
||||
+#define HTTP_HEADER_AUTH 4
|
||||
+#define HTTP_HEADER_FROM 8
|
||||
+#define HTTP_HEADER_CONTENT_LENGTH 16
|
||||
+#define HTTP_HEADER_TYPE 32
|
||||
+#define HTTP_HEADER_CONNECTION 64
|
||||
|
||||
#define HTTP_WRAPPER_HEADER_INIT 1
|
||||
#define HTTP_WRAPPER_REDIRECTED 2
|
||||
@@ -119,17 +120,15 @@ typedef struct _php_stream_http_response_header_info {
|
||||
size_t file_size;
|
||||
bool error;
|
||||
bool follow_location;
|
||||
- char location[HTTP_HEADER_BLOCK_SIZE];
|
||||
+ char *location;
|
||||
+ size_t location_len;
|
||||
} php_stream_http_response_header_info;
|
||||
|
||||
static void php_stream_http_response_header_info_init(
|
||||
php_stream_http_response_header_info *header_info)
|
||||
{
|
||||
- header_info->transfer_encoding = NULL;
|
||||
- header_info->file_size = 0;
|
||||
- header_info->error = false;
|
||||
+ memset(header_info, 0, sizeof(php_stream_http_response_header_info));
|
||||
header_info->follow_location = 1;
|
||||
- header_info->location[0] = '\0';
|
||||
}
|
||||
|
||||
/* Trim white spaces from response header line and update its length */
|
||||
@@ -255,7 +254,22 @@ static zend_string *php_stream_http_response_headers_parse(php_stream_wrapper *w
|
||||
* RFC 7238 defines 308: http://tools.ietf.org/html/rfc7238 */
|
||||
header_info->follow_location = 0;
|
||||
}
|
||||
- strlcpy(header_info->location, last_header_value, sizeof(header_info->location));
|
||||
+ size_t last_header_value_len = strlen(last_header_value);
|
||||
+ if (last_header_value_len > HTTP_HEADER_MAX_LOCATION_SIZE) {
|
||||
+ header_info->error = true;
|
||||
+ php_stream_wrapper_log_error(wrapper, options,
|
||||
+ "HTTP Location header size is over the limit of %d bytes",
|
||||
+ HTTP_HEADER_MAX_LOCATION_SIZE);
|
||||
+ zend_string_efree(last_header_line_str);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ if (header_info->location_len == 0) {
|
||||
+ header_info->location = emalloc(last_header_value_len + 1);
|
||||
+ } else if (header_info->location_len <= last_header_value_len) {
|
||||
+ header_info->location = erealloc(header_info->location, last_header_value_len + 1);
|
||||
+ }
|
||||
+ header_info->location_len = last_header_value_len;
|
||||
+ memcpy(header_info->location, last_header_value, last_header_value_len + 1);
|
||||
} else if (!strncasecmp(last_header_line, "Content-Type:", sizeof("Content-Type:")-1)) {
|
||||
php_stream_notify_info(context, PHP_STREAM_NOTIFY_MIME_TYPE_IS, last_header_value, 0);
|
||||
} else if (!strncasecmp(last_header_line, "Content-Length:", sizeof("Content-Length:")-1)) {
|
||||
@@ -538,6 +552,8 @@ finish:
|
||||
}
|
||||
}
|
||||
|
||||
+ php_stream_http_response_header_info_init(&header_info);
|
||||
+
|
||||
if (stream == NULL)
|
||||
goto out;
|
||||
|
||||
@@ -919,8 +935,6 @@ finish:
|
||||
}
|
||||
}
|
||||
|
||||
- php_stream_http_response_header_info_init(&header_info);
|
||||
-
|
||||
/* read past HTTP headers */
|
||||
while (!php_stream_eof(stream)) {
|
||||
size_t http_header_line_length;
|
||||
@@ -990,12 +1004,12 @@ finish:
|
||||
last_header_line_str, NULL, NULL, response_code, response_header, &header_info);
|
||||
}
|
||||
|
||||
- if (!reqok || (header_info.location[0] != '\0' && header_info.follow_location)) {
|
||||
+ if (!reqok || (header_info.location != NULL && header_info.follow_location)) {
|
||||
if (!header_info.follow_location || (((options & STREAM_ONLY_GET_HEADERS) || ignore_errors) && redirect_max <= 1)) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
- if (header_info.location[0] != '\0')
|
||||
+ if (header_info.location != NULL)
|
||||
php_stream_notify_info(context, PHP_STREAM_NOTIFY_REDIRECTED, header_info.location, 0);
|
||||
|
||||
php_stream_close(stream);
|
||||
@@ -1006,18 +1020,17 @@ finish:
|
||||
header_info.transfer_encoding = NULL;
|
||||
}
|
||||
|
||||
- if (header_info.location[0] != '\0') {
|
||||
+ if (header_info.location != NULL) {
|
||||
|
||||
- char new_path[HTTP_HEADER_BLOCK_SIZE];
|
||||
- char loc_path[HTTP_HEADER_BLOCK_SIZE];
|
||||
+ char *new_path = NULL;
|
||||
|
||||
- *new_path='\0';
|
||||
if (strlen(header_info.location) < 8 ||
|
||||
(strncasecmp(header_info.location, "http://", sizeof("http://")-1) &&
|
||||
strncasecmp(header_info.location, "https://", sizeof("https://")-1) &&
|
||||
strncasecmp(header_info.location, "ftp://", sizeof("ftp://")-1) &&
|
||||
strncasecmp(header_info.location, "ftps://", sizeof("ftps://")-1)))
|
||||
{
|
||||
+ char *loc_path = NULL;
|
||||
if (*header_info.location != '/') {
|
||||
if (*(header_info.location+1) != '\0' && resource->path) {
|
||||
char *s = strrchr(ZSTR_VAL(resource->path), '/');
|
||||
@@ -1035,31 +1048,35 @@ finish:
|
||||
if (resource->path &&
|
||||
ZSTR_VAL(resource->path)[0] == '/' &&
|
||||
ZSTR_VAL(resource->path)[1] == '\0') {
|
||||
- snprintf(loc_path, sizeof(loc_path) - 1, "%s%s",
|
||||
- ZSTR_VAL(resource->path), header_info.location);
|
||||
+ spprintf(&loc_path, 0, "%s%s", ZSTR_VAL(resource->path), header_info.location);
|
||||
} else {
|
||||
- snprintf(loc_path, sizeof(loc_path) - 1, "%s/%s",
|
||||
- ZSTR_VAL(resource->path), header_info.location);
|
||||
+ spprintf(&loc_path, 0, "%s/%s", ZSTR_VAL(resource->path), header_info.location);
|
||||
}
|
||||
} else {
|
||||
- snprintf(loc_path, sizeof(loc_path) - 1, "/%s", header_info.location);
|
||||
+ spprintf(&loc_path, 0, "/%s", header_info.location);
|
||||
}
|
||||
} else {
|
||||
- strlcpy(loc_path, header_info.location, sizeof(loc_path));
|
||||
+ loc_path = header_info.location;
|
||||
+ header_info.location = NULL;
|
||||
}
|
||||
if ((use_ssl && resource->port != 443) || (!use_ssl && resource->port != 80)) {
|
||||
- snprintf(new_path, sizeof(new_path) - 1, "%s://%s:%d%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), resource->port, loc_path);
|
||||
+ spprintf(&new_path, 0, "%s://%s:%d%s", ZSTR_VAL(resource->scheme),
|
||||
+ ZSTR_VAL(resource->host), resource->port, loc_path);
|
||||
} else {
|
||||
- snprintf(new_path, sizeof(new_path) - 1, "%s://%s%s", ZSTR_VAL(resource->scheme), ZSTR_VAL(resource->host), loc_path);
|
||||
+ spprintf(&new_path, 0, "%s://%s%s", ZSTR_VAL(resource->scheme),
|
||||
+ ZSTR_VAL(resource->host), loc_path);
|
||||
}
|
||||
+ efree(loc_path);
|
||||
} else {
|
||||
- strlcpy(new_path, header_info.location, sizeof(new_path));
|
||||
+ new_path = header_info.location;
|
||||
+ header_info.location = NULL;
|
||||
}
|
||||
|
||||
php_url_free(resource);
|
||||
/* check for invalid redirection URLs */
|
||||
if ((resource = php_url_parse(new_path)) == NULL) {
|
||||
php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path);
|
||||
+ efree(new_path);
|
||||
goto out;
|
||||
}
|
||||
|
||||
@@ -1071,6 +1088,7 @@ finish:
|
||||
while (s < e) { \
|
||||
if (iscntrl(*s)) { \
|
||||
php_stream_wrapper_log_error(wrapper, options, "Invalid redirect URL! %s", new_path); \
|
||||
+ efree(new_path); \
|
||||
goto out; \
|
||||
} \
|
||||
s++; \
|
||||
@@ -1086,6 +1104,7 @@ finish:
|
||||
stream = php_stream_url_wrap_http_ex(
|
||||
wrapper, new_path, mode, options, opened_path, context,
|
||||
--redirect_max, HTTP_WRAPPER_REDIRECTED, response_header STREAMS_CC);
|
||||
+ efree(new_path);
|
||||
} else {
|
||||
php_stream_wrapper_log_error(wrapper, options, "HTTP request failed! %s", tmp_line);
|
||||
}
|
||||
@@ -1098,6 +1117,10 @@ out:
|
||||
efree(http_header_line);
|
||||
}
|
||||
|
||||
+ if (header_info.location != NULL) {
|
||||
+ efree(header_info.location);
|
||||
+ }
|
||||
+
|
||||
if (resource) {
|
||||
php_url_free(resource);
|
||||
}
|
||||
diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..744cff9cc72
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-001.phpt
|
||||
@@ -0,0 +1,58 @@
|
||||
+--TEST--
|
||||
+GHSA-52jp-hrpf-2jff: HTTP stream wrapper truncate redirect location to 1024 bytes (success)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+$ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ $loc = str_repeat("y", 8000);
|
||||
+ fwrite($conn, "HTTP/1.0 301 Ok\r\nContent-Type: text/html;\r\nLocation: $loc\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ case STREAM_NOTIFY_REDIRECTED:
|
||||
+ echo "Redirected: ";
|
||||
+ var_dump($message);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+Found the mime-type: text/html;
|
||||
+Redirected: string(8000) "%s"
|
||||
+
|
||||
+Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: %s
|
||||
+string(0) ""
|
||||
+array(3) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 301 Ok"
|
||||
+ [1]=>
|
||||
+ string(24) "Content-Type: text/html;"
|
||||
+ [2]=>
|
||||
+ string(8010) "Location: %s"
|
||||
+}
|
||||
diff --git a/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt
|
||||
new file mode 100644
|
||||
index 00000000000..bc71fd4e411
|
||||
--- /dev/null
|
||||
+++ b/ext/standard/tests/http/ghsa-52jp-hrpf-2jff-002.phpt
|
||||
@@ -0,0 +1,55 @@
|
||||
+--TEST--
|
||||
+GHSA-52jp-hrpf-2jff: HTTP stream wrapper truncate redirect location to 1024 bytes (over limit)
|
||||
+--FILE--
|
||||
+<?php
|
||||
+$serverCode = <<<'CODE'
|
||||
+$ctxt = stream_context_create([
|
||||
+ "socket" => [
|
||||
+ "tcp_nodelay" => true
|
||||
+ ]
|
||||
+ ]);
|
||||
+
|
||||
+ $server = stream_socket_server(
|
||||
+ "tcp://127.0.0.1:0", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN, $ctxt);
|
||||
+ phpt_notify_server_start($server);
|
||||
+
|
||||
+ $conn = stream_socket_accept($server);
|
||||
+
|
||||
+ phpt_notify(message:"server-accepted");
|
||||
+
|
||||
+ $loc = str_repeat("y", 9000);
|
||||
+ fwrite($conn, "HTTP/1.0 301 Ok\r\nContent-Type: text/html;\r\nLocation: $loc\r\n\r\nbody\r\n");
|
||||
+CODE;
|
||||
+
|
||||
+$clientCode = <<<'CODE'
|
||||
+ function stream_notification_callback($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
|
||||
+ switch($notification_code) {
|
||||
+ case STREAM_NOTIFY_MIME_TYPE_IS:
|
||||
+ echo "Found the mime-type: ", $message, PHP_EOL;
|
||||
+ break;
|
||||
+ case STREAM_NOTIFY_REDIRECTED:
|
||||
+ echo "Redirected: ";
|
||||
+ var_dump($message);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ $ctx = stream_context_create();
|
||||
+ stream_context_set_params($ctx, array("notification" => "stream_notification_callback"));
|
||||
+ var_dump(trim(file_get_contents("http://{{ ADDR }}", false, $ctx)));
|
||||
+ var_dump($http_response_header);
|
||||
+CODE;
|
||||
+
|
||||
+include sprintf("%s/../../../openssl/tests/ServerClientTestCase.inc", __DIR__);
|
||||
+ServerClientTestCase::getInstance()->run($clientCode, $serverCode);
|
||||
+?>
|
||||
+--EXPECTF--
|
||||
+Found the mime-type: text/html;
|
||||
+
|
||||
+Warning: file_get_contents(http://127.0.0.1:%d): Failed to open stream: HTTP Location header size is over the limit of 8182 bytes in %s
|
||||
+string(0) ""
|
||||
+array(2) {
|
||||
+ [0]=>
|
||||
+ string(15) "HTTP/1.0 301 Ok"
|
||||
+ [1]=>
|
||||
+ string(24) "Content-Type: text/html;"
|
||||
+}
|
||||
--
|
||||
2.48.1
|
||||
|
25
php.spec
25
php.spec
@ -62,7 +62,7 @@
|
||||
Summary: PHP scripting language for creating dynamic web sites
|
||||
Name: php
|
||||
Version: %{upver}%{?rcver:~%{rcver}}
|
||||
Release: 2%{?dist}
|
||||
Release: 3%{?dist}
|
||||
# All files licensed under PHP version 3.01, except
|
||||
# Zend is licensed under Zend
|
||||
# TSRM is licensed under BSD
|
||||
@ -125,6 +125,7 @@ Patch51: php-8.0.13-crypt.patch
|
||||
# Upstream fixes (100+)
|
||||
|
||||
# Security fixes (200+)
|
||||
# From https://github.com/remicollet/php-src-security
|
||||
Patch200: php-cve-2024-2756.patch
|
||||
Patch201: php-cve-2024-3096.patch
|
||||
Patch202: php-cve-2024-5458.patch
|
||||
@ -138,6 +139,11 @@ Patch209: php-cve-2024-8932.patch
|
||||
Patch210: php-cve-2024-11233.patch
|
||||
Patch211: php-ghsa-4w77-75f9-2c8w.patch
|
||||
Patch212: php-cve-2024-8929.patch
|
||||
Patch213: php-cve-2025-1217.patch
|
||||
Patch214: php-cve-2025-1734.patch
|
||||
Patch215: php-cve-2025-1861.patch
|
||||
Patch216: php-cve-2025-1736.patch
|
||||
Patch217: php-cve-2025-1219.patch
|
||||
|
||||
# Fixes for tests (300+)
|
||||
# Factory is droped from system tzdata
|
||||
@ -753,6 +759,11 @@ rm ext/openssl/tests/p12_with_extra_certs.p12
|
||||
%patch -P210 -p1 -b .cve11233
|
||||
%patch -P211 -p1 -b .ghsa4w77
|
||||
%patch -P212 -p1 -b .cve8929
|
||||
%patch -P213 -p1 -b .cve1217
|
||||
%patch -P214 -p1 -b .cve1734
|
||||
%patch -P215 -p1 -b .cve1861
|
||||
%patch -P216 -p1 -b .cve1736
|
||||
%patch -P217 -p1 -b .cve1219
|
||||
|
||||
# Fixes for tests
|
||||
%patch -P300 -p1 -b .datetests
|
||||
@ -1561,6 +1572,18 @@ systemctl try-restart php-fpm.service >/dev/null 2>&1 || :
|
||||
|
||||
|
||||
%changelog
|
||||
* Thu Mar 13 2025 Remi Collet <rcollet@redhat.com> - 8.0.30-3
|
||||
- Fix libxml streams use wrong `content-type` header when requesting a redirected resource
|
||||
CVE-2025-1219
|
||||
- Fix Stream HTTP wrapper header check might omit basic auth header
|
||||
CVE-2025-1736
|
||||
- Fix Stream HTTP wrapper truncate redirect location to 1024 bytes
|
||||
CVE-2025-1861
|
||||
- Fix Streams HTTP wrapper does not fail for headers without colon
|
||||
CVE-2025-1734
|
||||
- Fix Header parser of `http` stream wrapper does not handle folded headers
|
||||
CVE-2025-1217
|
||||
|
||||
* Tue Jan 21 2025 Remi Collet <rcollet@redhat.com> - 8.0.30-2
|
||||
- Fix Leak partial content of the heap through heap buffer over-read
|
||||
CVE-2024-8929
|
||||
|
Loading…
Reference in New Issue
Block a user