mirror of
https://github.com/php/php-src.git
synced 2026-04-23 16:08:35 +02:00
Merge branch 'PHP-5.5' into PHP-5.6
Conflicts: ext/openssl/xp_ssl.c
This commit is contained in:
@@ -5,6 +5,9 @@
|
||||
- ODBC:
|
||||
. Bug #68964 (Allowed memory size exhausted with odbc_exec). (Anatol)
|
||||
|
||||
- OpenSSL:
|
||||
. Fix bug #61285, #68329, #68046, #41631: encrypted streams don't observe
|
||||
socket timeouts (Brad Broerman)
|
||||
|
||||
?? Feb 2015, PHP 5.6.6
|
||||
|
||||
|
||||
+176
-65
@@ -78,6 +78,9 @@ extern php_stream* php_openssl_get_stream_from_ssl_handle(const SSL *ssl);
|
||||
extern int php_openssl_x509_fingerprint(X509 *peer, const char *method, zend_bool raw, char **out, int *out_len TSRMLS_DC);
|
||||
extern int php_openssl_get_ssl_stream_data_index();
|
||||
extern int php_openssl_get_x509_list_id(void);
|
||||
static struct timeval subtract_timeval( struct timeval a, struct timeval b );
|
||||
static int compare_timeval( struct timeval a, struct timeval b );
|
||||
static size_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, size_t count TSRMLS_DC);
|
||||
|
||||
php_stream_ops php_openssl_socket_ops;
|
||||
|
||||
@@ -1679,16 +1682,9 @@ static int php_openssl_enable_crypto(php_stream *stream,
|
||||
|
||||
if (has_timeout) {
|
||||
gettimeofday(&cur_time, NULL);
|
||||
elapsed_time.tv_sec = cur_time.tv_sec - start_time.tv_sec;
|
||||
elapsed_time.tv_usec = cur_time.tv_usec - start_time.tv_usec;
|
||||
if (cur_time.tv_usec < start_time.tv_usec) {
|
||||
elapsed_time.tv_sec -= 1L;
|
||||
elapsed_time.tv_usec += 1000000L;
|
||||
}
|
||||
elapsed_time = subtract_timeval( cur_time, start_time );
|
||||
|
||||
if (elapsed_time.tv_sec > timeout->tv_sec ||
|
||||
(elapsed_time.tv_sec == timeout->tv_sec &&
|
||||
elapsed_time.tv_usec > timeout->tv_usec)) {
|
||||
if (compare_timeval( elapsed_time, *timeout) > 0) {
|
||||
php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL: Handshake timed out");
|
||||
return -1;
|
||||
}
|
||||
@@ -1704,12 +1700,7 @@ static int php_openssl_enable_crypto(php_stream *stream,
|
||||
struct timeval left_time;
|
||||
|
||||
if (has_timeout) {
|
||||
left_time.tv_sec = timeout->tv_sec - elapsed_time.tv_sec;
|
||||
left_time.tv_usec = timeout->tv_usec - elapsed_time.tv_usec;
|
||||
if (timeout->tv_usec < elapsed_time.tv_usec) {
|
||||
left_time.tv_sec -= 1L;
|
||||
left_time.tv_usec += 1000000L;
|
||||
}
|
||||
left_time = subtract_timeval( *timeout, elapsed_time );
|
||||
}
|
||||
php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
|
||||
(POLLIN|POLLPRI) : POLLOUT, has_timeout ? &left_time : NULL);
|
||||
@@ -1774,75 +1765,169 @@ static int php_openssl_enable_crypto(php_stream *stream,
|
||||
return -1;
|
||||
}
|
||||
|
||||
static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
|
||||
static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)/* {{{ */
|
||||
{
|
||||
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
|
||||
int didwrite;
|
||||
|
||||
if (sslsock->ssl_active) {
|
||||
int retry = 1;
|
||||
|
||||
do {
|
||||
didwrite = SSL_write(sslsock->ssl_handle, buf, count);
|
||||
|
||||
if (didwrite <= 0) {
|
||||
retry = handle_ssl_error(stream, didwrite, 0 TSRMLS_CC);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} while(retry);
|
||||
|
||||
if (didwrite > 0) {
|
||||
php_stream_notify_progress_increment(stream->context, didwrite, 0);
|
||||
}
|
||||
} else {
|
||||
didwrite = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
|
||||
}
|
||||
|
||||
if (didwrite < 0) {
|
||||
didwrite = 0;
|
||||
}
|
||||
|
||||
return didwrite;
|
||||
return php_openssl_sockop_io(1, stream, buf, count TSRMLS_CC);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t count TSRMLS_DC) /* {{{ */
|
||||
static size_t php_openssl_sockop_write(php_stream *stream, const char *buf, size_t count TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
return php_openssl_sockop_io(0, stream, (char*)buf, count TSRMLS_CC);
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
/**
|
||||
* Factored out common functionality (blocking, timeout, loop management) for read and write.
|
||||
* Perform IO (read or write) to an SSL socket. If we have a timeout, we switch to non-blocking mode
|
||||
* for the duration of the operation, using select to do our waits. If we time out, or we have an error
|
||||
* report that back to PHP
|
||||
*
|
||||
*/
|
||||
static size_t php_openssl_sockop_io(int read, php_stream *stream, char *buf, size_t count TSRMLS_DC)
|
||||
{
|
||||
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
|
||||
int nr_bytes = 0;
|
||||
|
||||
|
||||
/* Only do this if SSL is active. */
|
||||
if (sslsock->ssl_active) {
|
||||
int retry = 1;
|
||||
struct timeval start_time;
|
||||
struct timeval *timeout;
|
||||
int blocked = sslsock->s.is_blocked;
|
||||
int has_timeout = 0;
|
||||
|
||||
/* Begin by making the socket non-blocking. This allows us to check the timeout. */
|
||||
if (SUCCESS == php_set_sock_blocking(sslsock->s.socket, 0 TSRMLS_CC)) {
|
||||
sslsock->s.is_blocked = 0;
|
||||
}
|
||||
|
||||
/* Get the timeout value (and make sure we are to check it. */
|
||||
timeout = sslsock->is_client ? &sslsock->connect_timeout : &sslsock->s.timeout;
|
||||
has_timeout = !sslsock->s.is_blocked && (timeout->tv_sec || timeout->tv_usec);
|
||||
|
||||
/* gettimeofday is not monotonic; using it here is not strictly correct */
|
||||
if (has_timeout) {
|
||||
gettimeofday(&start_time, NULL);
|
||||
}
|
||||
|
||||
/* Main IO loop. */
|
||||
do {
|
||||
nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
|
||||
struct timeval cur_time, elapsed_time, left_time;
|
||||
|
||||
/* If we have a timeout to check, figure out how much time has elapsed since we started. */
|
||||
if (has_timeout) {
|
||||
gettimeofday(&cur_time, NULL);
|
||||
|
||||
if (sslsock->reneg && sslsock->reneg->should_close) {
|
||||
/* renegotiation rate limiting triggered */
|
||||
php_stream_xport_shutdown(stream, (stream_shutdown_t)SHUT_RDWR TSRMLS_CC);
|
||||
nr_bytes = 0;
|
||||
stream->eof = 1;
|
||||
break;
|
||||
} else if (nr_bytes <= 0) {
|
||||
retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC);
|
||||
stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
|
||||
|
||||
} else {
|
||||
/* we got the data */
|
||||
break;
|
||||
/* Determine how much time we've taken so far. */
|
||||
elapsed_time = subtract_timeval( cur_time, start_time );
|
||||
|
||||
/* and return an error if we've taken too long. */
|
||||
if (compare_timeval( elapsed_time, *timeout) > 0 ) {
|
||||
/* If the socket was originally blocking, set it back. */
|
||||
if (blocked) {
|
||||
php_set_sock_blocking(sslsock->s.socket, 1 TSRMLS_CC);
|
||||
sslsock->s.is_blocked = 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Now, do the IO operation. Don't block if we can't complete... */
|
||||
if (read) {
|
||||
nr_bytes = SSL_read(sslsock->ssl_handle, buf, count);
|
||||
|
||||
if (sslsock->reneg && sslsock->reneg->should_close) {
|
||||
/* renegotiation rate limiting triggered */
|
||||
php_stream_xport_shutdown(stream, (stream_shutdown_t)SHUT_RDWR TSRMLS_CC);
|
||||
nr_bytes = 0;
|
||||
stream->eof = 1;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
nr_bytes = SSL_write(sslsock->ssl_handle, buf, count);
|
||||
}
|
||||
|
||||
/* Now, how much time until we time out? */
|
||||
if (has_timeout) {
|
||||
left_time = subtract_timeval( *timeout, elapsed_time );
|
||||
}
|
||||
|
||||
/* If we didn't do anything on the last loop (or an error) check to see if we should retry or exit. */
|
||||
if (nr_bytes <= 0) {
|
||||
|
||||
/* Get the error code from SSL, and check to see if it's an error or not. */
|
||||
int err = SSL_get_error(sslsock->ssl_handle, nr_bytes );
|
||||
retry = handle_ssl_error(stream, nr_bytes, 0 TSRMLS_CC);
|
||||
|
||||
/* If we get this (the above doesn't check) then we'll retry as well. */
|
||||
if (errno == EAGAIN && err == SSL_ERROR_WANT_READ && read) {
|
||||
retry = 1;
|
||||
}
|
||||
if (errno == EAGAIN && SSL_ERROR_WANT_WRITE && read == 0) {
|
||||
retry = 1;
|
||||
}
|
||||
|
||||
/* Also, on reads, we may get this condition on an EOF. We should check properly. */
|
||||
if (read) {
|
||||
stream->eof = (retry == 0 && errno != EAGAIN && !SSL_pending(sslsock->ssl_handle));
|
||||
}
|
||||
|
||||
/* Now, if we have to wait some time, and we're supposed to be blocking, wait for the socket to become
|
||||
* available. Now, php_pollfd_for uses select to wait up to our time_left value only...
|
||||
*/
|
||||
if (retry && blocked) {
|
||||
if (read) {
|
||||
php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_WRITE) ?
|
||||
(POLLOUT|POLLPRI) : (POLLIN|POLLPRI), has_timeout ? &left_time : NULL);
|
||||
} else {
|
||||
php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
|
||||
(POLLIN|POLLPRI) : (POLLOUT|POLLPRI), has_timeout ? &left_time : NULL);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* Else, if we got bytes back, check for possible errors. */
|
||||
int err = SSL_get_error(sslsock->ssl_handle, nr_bytes );
|
||||
|
||||
/* If we didn't get any error, then let's return it to PHP. */
|
||||
if (err == SSL_ERROR_NONE)
|
||||
break;
|
||||
|
||||
/* Otherwise, we need to wait again (up to time_left or we get an error) */
|
||||
if (blocked)
|
||||
if (read) {
|
||||
php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_WRITE) ?
|
||||
(POLLOUT|POLLPRI) : (POLLIN|POLLPRI), has_timeout ? &left_time : NULL);
|
||||
} else {
|
||||
php_pollfd_for(sslsock->s.socket, (err == SSL_ERROR_WANT_READ) ?
|
||||
(POLLIN|POLLPRI) : (POLLOUT|POLLPRI), has_timeout ? &left_time : NULL);
|
||||
}
|
||||
}
|
||||
/* Finally, we keep going until we got data, and an SSL_ERROR_NONE, unless we had an error. */
|
||||
} while (retry);
|
||||
|
||||
/* Tell PHP if we read / wrote bytes. */
|
||||
if (nr_bytes > 0) {
|
||||
php_stream_notify_progress_increment(stream->context, nr_bytes, 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
|
||||
|
||||
/* And if we were originally supposed to be blocking, let's reset the socket to that. */
|
||||
if (blocked) {
|
||||
php_set_sock_blocking(sslsock->s.socket, 1 TSRMLS_CC);
|
||||
sslsock->s.is_blocked = 1;
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* This block is if we had no timeout... We will just sit and wait forever on the IO operation.
|
||||
*/
|
||||
if (read) {
|
||||
nr_bytes = php_stream_socket_ops.read(stream, buf, count TSRMLS_CC);
|
||||
} else {
|
||||
nr_bytes = php_stream_socket_ops.write(stream, buf, count TSRMLS_CC);
|
||||
}
|
||||
}
|
||||
|
||||
/* PHP doesn't expect a negative return. */
|
||||
if (nr_bytes < 0) {
|
||||
nr_bytes = 0;
|
||||
}
|
||||
@@ -1851,6 +1936,32 @@ static size_t php_openssl_sockop_read(php_stream *stream, char *buf, size_t coun
|
||||
}
|
||||
/* }}} */
|
||||
|
||||
struct timeval subtract_timeval( struct timeval a, struct timeval b )
|
||||
{
|
||||
struct timeval difference;
|
||||
|
||||
difference.tv_sec = a.tv_sec - b.tv_sec;
|
||||
difference.tv_usec = a.tv_usec - b.tv_usec;
|
||||
|
||||
if (a.tv_usec < b.tv_usec) {
|
||||
b.tv_sec -= 1L;
|
||||
b.tv_usec += 1000000L;
|
||||
}
|
||||
|
||||
return difference;
|
||||
}
|
||||
|
||||
int compare_timeval( struct timeval a, struct timeval b )
|
||||
{
|
||||
if (a.tv_sec > b.tv_sec || (a.tv_sec == b.tv_sec && a.tv_usec > b.tv_usec) ) {
|
||||
return 1;
|
||||
} else if( a.tv_sec == b.tv_sec && a.tv_usec == b.tv_usec ) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int php_openssl_sockop_close(php_stream *stream, int close_handle TSRMLS_DC) /* {{{ */
|
||||
{
|
||||
php_openssl_netstream_data_t *sslsock = (php_openssl_netstream_data_t*)stream->abstract;
|
||||
@@ -2168,7 +2279,7 @@ static long get_crypto_method(php_stream_context *ctx, long crypto_method)
|
||||
if (ctx && php_stream_context_get_option(ctx, "ssl", "crypto_method", &val) == SUCCESS) {
|
||||
convert_to_long_ex(val);
|
||||
crypto_method = (long)Z_LVAL_PP(val);
|
||||
crypto_method |= STREAM_CRYPTO_IS_CLIENT;
|
||||
crypto_method |= STREAM_CRYPTO_IS_CLIENT;
|
||||
}
|
||||
|
||||
return crypto_method;
|
||||
|
||||
Reference in New Issue
Block a user