@@ -146,6 +146,7 @@ static zend_result php_stream_handle_proxy_authorization_header(const char *s, s
146
146
typedef struct _php_stream_http_response_header_info {
147
147
php_stream_filter * transfer_encoding ;
148
148
size_t file_size ;
149
+ bool error ;
149
150
bool follow_location ;
150
151
char location [HTTP_HEADER_BLOCK_SIZE ];
151
152
} php_stream_http_response_header_info ;
@@ -155,6 +156,7 @@ static void php_stream_http_response_header_info_init(
155
156
{
156
157
header_info -> transfer_encoding = NULL ;
157
158
header_info -> file_size = 0 ;
159
+ header_info -> error = false;
158
160
header_info -> follow_location = 1 ;
159
161
header_info -> location [0 ] = '\0' ;
160
162
}
@@ -192,10 +194,11 @@ static bool php_stream_http_response_header_trim(char *http_header_line,
192
194
/* Process folding headers of the current line and if there are none, parse last full response
193
195
* header line. It returns NULL if the last header is finished, otherwise it returns updated
194
196
* last header line. */
195
- static zend_string * php_stream_http_response_headers_parse (php_stream * stream ,
196
- php_stream_context * context , int options , zend_string * last_header_line_str ,
197
- char * header_line , size_t * header_line_length , int response_code ,
198
- zval * response_header , php_stream_http_response_header_info * header_info )
197
+ static zend_string * php_stream_http_response_headers_parse (php_stream_wrapper * wrapper ,
198
+ php_stream * stream , php_stream_context * context , int options ,
199
+ zend_string * last_header_line_str , char * header_line , size_t * header_line_length ,
200
+ int response_code , zval * response_header ,
201
+ php_stream_http_response_header_info * header_info )
199
202
{
200
203
char * last_header_line = ZSTR_VAL (last_header_line_str );
201
204
size_t last_header_line_length = ZSTR_LEN (last_header_line_str );
@@ -237,6 +240,19 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
237
240
/* Find header separator position. */
238
241
char * last_header_value = memchr (last_header_line , ':' , last_header_line_length );
239
242
if (last_header_value ) {
243
+ /* Verify there is no space in header name */
244
+ char * last_header_name = last_header_line + 1 ;
245
+ while (last_header_name < last_header_value ) {
246
+ if (* last_header_name == ' ' || * last_header_name == '\t' ) {
247
+ header_info -> error = true;
248
+ php_stream_wrapper_log_error (wrapper , options ,
249
+ "HTTP invalid response format (space in header name)!" );
250
+ zend_string_efree (last_header_line_str );
251
+ return NULL ;
252
+ }
253
+ ++ last_header_name ;
254
+ }
255
+
240
256
last_header_value ++ ; /* Skip ':'. */
241
257
242
258
/* Strip leading whitespace. */
@@ -245,9 +261,12 @@ static zend_string *php_stream_http_response_headers_parse(php_stream *stream,
245
261
last_header_value ++ ;
246
262
}
247
263
} else {
248
- /* There is no colon. Set the value to the end of the header line, which is effectively
249
- * an empty string. */
250
- last_header_value = last_header_line_end ;
264
+ /* There is no colon which means invalid response so error. */
265
+ header_info -> error = true;
266
+ php_stream_wrapper_log_error (wrapper , options ,
267
+ "HTTP invalid response format (no colon in header line)!" );
268
+ zend_string_efree (last_header_line_str );
269
+ return NULL ;
251
270
}
252
271
253
272
bool store_header = true;
@@ -944,10 +963,16 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
944
963
945
964
if (last_header_line_str != NULL ) {
946
965
/* Parse last header line. */
947
- last_header_line_str = php_stream_http_response_headers_parse (stream , context ,
948
- options , last_header_line_str , http_header_line , & http_header_line_length ,
949
- response_code , response_header , & header_info );
950
- if (last_header_line_str != NULL ) {
966
+ last_header_line_str = php_stream_http_response_headers_parse (wrapper , stream ,
967
+ context , options , last_header_line_str , http_header_line ,
968
+ & http_header_line_length , response_code , response_header , & header_info );
969
+ if (EXPECTED (last_header_line_str == NULL )) {
970
+ if (UNEXPECTED (header_info .error )) {
971
+ php_stream_close (stream );
972
+ stream = NULL ;
973
+ goto out ;
974
+ }
975
+ } else {
951
976
/* Folding header present so continue. */
952
977
continue ;
953
978
}
@@ -977,8 +1002,8 @@ static php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper,
977
1002
978
1003
/* If the stream was closed early, we still want to process the last line to keep BC. */
979
1004
if (last_header_line_str != NULL ) {
980
- php_stream_http_response_headers_parse (stream , context , options , last_header_line_str ,
981
- NULL , NULL , response_code , response_header , & header_info );
1005
+ php_stream_http_response_headers_parse (wrapper , stream , context , options ,
1006
+ last_header_line_str , NULL , NULL , response_code , response_header , & header_info );
982
1007
}
983
1008
984
1009
if (!reqok || (header_info .location [0 ] != '\0' && header_info .follow_location )) {
0 commit comments