summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStanislav Malyshev <stas@php.net>2015-04-12 01:30:33 -0700
committerStanislav Malyshev <stas@php.net>2015-04-12 01:30:33 -0700
commitd82d68742c69fc20a5180a2dbcca4cac52435931 (patch)
treec16304e92a67258296bb0d54b4cd9b60307d78cc
parent1defbb25ed69e7a1a90e2bcb2ee3b9190ea06577 (diff)
downloadphp-git-d82d68742c69fc20a5180a2dbcca4cac52435931.tar.gz
Fix bug #69337 (php_stream_url_wrap_http_ex() type-confusion vulnerability)
-rw-r--r--ext/standard/http_fopen_wrapper.c79
1 files changed, 43 insertions, 36 deletions
diff --git a/ext/standard/http_fopen_wrapper.c b/ext/standard/http_fopen_wrapper.c
index 13614ae3b7..9c99496ed4 100644
--- a/ext/standard/http_fopen_wrapper.c
+++ b/ext/standard/http_fopen_wrapper.c
@@ -19,7 +19,7 @@
| Sara Golemon <pollita@php.net> |
+----------------------------------------------------------------------+
*/
-/* $Id$ */
+/* $Id$ */
#include "php.h"
#include "php_globals.h"
@@ -152,7 +152,7 @@ php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, char *path,
}
if (strncasecmp(resource->scheme, "http", sizeof("http")) && strncasecmp(resource->scheme, "https", sizeof("https"))) {
- if (!context ||
+ if (!context ||
php_stream_context_get_option(context, wrapper->wops->label, "proxy", &tmpzval) == FAILURE ||
Z_TYPE_PP(tmpzval) != IS_STRING ||
Z_STRLEN_PP(tmpzval) <= 0) {
@@ -168,7 +168,7 @@ php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, char *path,
transport_string = estrndup(Z_STRVAL_PP(tmpzval), Z_STRLEN_PP(tmpzval));
} else {
/* Normal http request (possibly with proxy) */
-
+
if (strpbrk(mode, "awx+")) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "HTTP wrapper does not support writeable connections");
php_url_free(resource);
@@ -207,11 +207,11 @@ php_stream *php_stream_url_wrap_http_ex(php_stream_wrapper *wrapper, char *path,
stream = php_stream_xport_create(transport_string, transport_len, options,
STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT,
NULL, &timeout, context, &errstr, NULL);
-
+
if (stream) {
php_stream_set_option(stream, PHP_STREAM_OPTION_READ_TIMEOUT, 0, &timeout);
}
-
+
if (errstr) {
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "%s", errstr);
efree(errstr);
@@ -328,7 +328,7 @@ finish:
/* avoid buffering issues while reading header */
if (options & STREAM_WILL_CAST)
chunk_size = php_stream_set_chunk_size(stream, 1);
-
+
/* avoid problems with auto-detecting when reading the headers -> the headers
* are always in canonical \r\n format */
eol_detect = stream->flags & (PHP_STREAM_FLAG_DETECT_EOL | PHP_STREAM_FLAG_EOL_MAC);
@@ -359,7 +359,7 @@ finish:
}
}
}
-
+
if (context && php_stream_context_get_option(context, "http", "protocol_version", &tmpzval) == SUCCESS) {
SEPARATE_ZVAL(tmpzval);
convert_to_double_ex(tmpzval);
@@ -420,7 +420,7 @@ finish:
if (context && php_stream_context_get_option(context, "http", "header", &tmpzval) == SUCCESS) {
tmp = NULL;
-
+
if (Z_TYPE_PP(tmpzval) == IS_ARRAY) {
HashPosition pos;
zval **tmpheader = NULL;
@@ -460,42 +460,42 @@ finish:
strip_header(user_headers, tmp, "content-type:");
}
- if ((s = strstr(tmp, "user-agent:")) &&
- (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
+ if ((s = strstr(tmp, "user-agent:")) &&
+ (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
*(s-1) == '\t' || *(s-1) == ' ')) {
have_header |= HTTP_HEADER_USER_AGENT;
}
if ((s = strstr(tmp, "host:")) &&
- (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
+ (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
*(s-1) == '\t' || *(s-1) == ' ')) {
have_header |= HTTP_HEADER_HOST;
}
if ((s = strstr(tmp, "from:")) &&
- (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
+ (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
*(s-1) == '\t' || *(s-1) == ' ')) {
have_header |= HTTP_HEADER_FROM;
}
if ((s = strstr(tmp, "authorization:")) &&
- (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
+ (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
*(s-1) == '\t' || *(s-1) == ' ')) {
have_header |= HTTP_HEADER_AUTH;
}
if ((s = strstr(tmp, "content-length:")) &&
- (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
+ (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
*(s-1) == '\t' || *(s-1) == ' ')) {
have_header |= HTTP_HEADER_CONTENT_LENGTH;
}
if ((s = strstr(tmp, "content-type:")) &&
- (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
+ (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
*(s-1) == '\t' || *(s-1) == ' ')) {
have_header |= HTTP_HEADER_TYPE;
}
/* remove Proxy-Authorization header */
if (use_proxy && use_ssl && (s = strstr(tmp, "proxy-authorization:")) &&
- (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
+ (s == tmp || *(s-1) == '\r' || *(s-1) == '\n' ||
*(s-1) == '\t' || *(s-1) == ' ')) {
char *p = s + sizeof("proxy-authorization:") - 1;
-
+
while (s > tmp && (*(s-1) == ' ' || *(s-1) == '\t')) s--;
while (*p != 0 && *p != '\r' && *p != '\n') p++;
while (*p == '\r' || *p == '\n') p++;
@@ -534,7 +534,7 @@ finish:
}
tmp = (char*)php_base64_encode((unsigned char*)scratch, strlen(scratch), NULL);
-
+
if (snprintf(scratch, scratch_len, "Authorization: Basic %s\r\n", tmp) > 0) {
php_stream_write(stream, scratch, strlen(scratch));
php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, NULL, 0);
@@ -552,7 +552,7 @@ finish:
/* Send Host: header so name-based virtual hosts work */
if ((have_header & HTTP_HEADER_HOST) == 0) {
- if ((use_ssl && resource->port != 443 && resource->port != 0) ||
+ if ((use_ssl && resource->port != 443 && resource->port != 0) ||
(!use_ssl && resource->port != 80 && resource->port != 0)) {
if (snprintf(scratch, scratch_len, "Host: %s:%i\r\n", resource->host, resource->port) > 0)
php_stream_write(stream, scratch, strlen(scratch));
@@ -563,7 +563,7 @@ finish:
}
}
- if (context &&
+ if (context &&
php_stream_context_get_option(context, "http", "user_agent", &ua_zval) == SUCCESS &&
Z_TYPE_PP(ua_zval) == IS_STRING) {
ua_str = Z_STRVAL_PP(ua_zval);
@@ -575,9 +575,9 @@ finish:
#define _UA_HEADER "User-Agent: %s\r\n"
char *ua;
size_t ua_len;
-
+
ua_len = sizeof(_UA_HEADER) + strlen(ua_str);
-
+
/* ensure the header is only sent if user_agent is not blank */
if (ua_len > sizeof(_UA_HEADER)) {
ua = emalloc(ua_len + 1);
@@ -591,7 +591,7 @@ finish:
if (ua) {
efree(ua);
}
- }
+ }
}
if (user_headers) {
@@ -649,8 +649,12 @@ finish:
{
zval **rh;
- zend_hash_find(EG(active_symbol_table), "http_response_header", sizeof("http_response_header"), (void **) &rh);
+ if(zend_hash_find(EG(active_symbol_table), "http_response_header", sizeof("http_response_header"), (void **) &rh) != SUCCESS || Z_TYPE_PP(rh) != IS_ARRAY) {
+ php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "HTTP request failed, http_response_header overwritten");
+ goto out;
+ }
response_header = *rh;
+ Z_ADDREF_P(response_header);
}
if (!php_stream_eof(stream)) {
@@ -706,9 +710,9 @@ finish:
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "HTTP request failed, unexpected end of socket!");
goto out;
}
-
+
/* read past HTTP headers */
-
+
http_header_line = emalloc(HTTP_HEADER_BLOCK_SIZE);
while (!body && !php_stream_eof(stream)) {
@@ -738,7 +742,7 @@ finish:
follow_location = Z_LVAL_PP(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)
+ 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;
@@ -778,7 +782,7 @@ finish:
MAKE_STD_ZVAL(http_header);
ZVAL_STRINGL(http_header, http_header_line, http_header_line_length, 1);
-
+
zend_hash_next_index_insert(Z_ARRVAL_P(response_header), &http_header, sizeof(zval *), NULL);
}
} else {
@@ -803,10 +807,10 @@ finish:
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(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 (*location != '/') {
if (*(location+1) != '\0' && resource->path) {
@@ -820,7 +824,7 @@ finish:
*s = '/';
}
}
- s[1] = '\0';
+ s[1] = '\0';
if (resource->path && *(resource->path) == '/' && *(resource->path + 1) == '\0') {
snprintf(loc_path, sizeof(loc_path) - 1, "%s%s", resource->path, location);
} else {
@@ -893,18 +897,21 @@ out:
if (stream) {
if (header_init) {
- zval_add_ref(&response_header);
stream->wrapperdata = response_header;
+ } else {
+ if(response_header) {
+ Z_DELREF_P(response_header);
+ }
}
php_stream_notify_progress_init(context, 0, file_size);
-
+
/* Restore original chunk size now that we're done with headers */
if (options & STREAM_WILL_CAST)
php_stream_set_chunk_size(stream, chunk_size);
/* restore the users auto-detect-line-endings setting */
stream->flags |= eol_detect;
-
+
/* as far as streams are concerned, we are now at the start of
* the stream */
stream->position = 0;