Skip to content

Commit fde2ae3

Browse files
committed
Merge branch 'PHP-7.1' into PHP-7.2
2 parents 42b769b + bb4a2e8 commit fde2ae3

File tree

2 files changed

+69
-6
lines changed

2 files changed

+69
-6
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ PHP NEWS
1010
- FCGI:
1111
. Fixed #76948 (Failed shutdown/reboot or end session in Windows). (Anatol)
1212

13+
- FTP:
14+
. Fixed bug #76972 (Data truncation due to forceful ssl socket shutdown).
15+
(Manuel Mausz)
16+
1317
- Reflection:
1418
. Fixed bug #76936 (Objects cannot access their private attributes while
1519
handling reflection errors). (Nikita)

ext/ftp/ftp.c

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@
5959

6060
#ifdef HAVE_FTP_SSL
6161
#include <openssl/ssl.h>
62+
#include <openssl/err.h>
6263
#endif
6364

6465
#include "ftp.h"
@@ -100,6 +101,11 @@ static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
100101
/* generic file lister */
101102
static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len);
102103

104+
#ifdef HAVE_FTP_SSL
105+
/* shuts down a TLS/SSL connection */
106+
static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle);
107+
#endif
108+
103109
/* IP and port conversion box */
104110
union ipbox {
105111
struct in_addr ia[2];
@@ -173,8 +179,7 @@ ftp_close(ftpbuf_t *ftp)
173179
if (ftp->fd != -1) {
174180
#ifdef HAVE_FTP_SSL
175181
if (ftp->ssl_active) {
176-
SSL_shutdown(ftp->ssl_handle);
177-
SSL_free(ftp->ssl_handle);
182+
ftp_ssl_shutdown(ftp, ftp->fd, ftp->ssl_handle);
178183
}
179184
#endif
180185
closesocket(ftp->fd);
@@ -1881,6 +1886,62 @@ data_accept(databuf_t *data, ftpbuf_t *ftp)
18811886
}
18821887
/* }}} */
18831888

1889+
/* {{{ ftp_ssl_shutdown
1890+
*/
1891+
#ifdef HAVE_FTP_SSL
1892+
static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle) {
1893+
/* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train
1894+
the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the
1895+
server might get an ECONNRESET which might lead to data truncation on server side.
1896+
*/
1897+
char buf[256]; /* We will use this for the OpenSSL error buffer, so it has
1898+
to be at least 256 bytes long.*/
1899+
int done = 1, err, nread;
1900+
unsigned long sslerror;
1901+
1902+
err = SSL_shutdown(ssl_handle);
1903+
if (err < 0) {
1904+
php_error_docref(NULL, E_WARNING, "SSL_shutdown failed");
1905+
}
1906+
else if (err == 0) {
1907+
/* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */
1908+
done = 0;
1909+
}
1910+
1911+
while (!done) {
1912+
if (data_available(ftp, fd)) {
1913+
ERR_clear_error();
1914+
nread = SSL_read(ssl_handle, buf, sizeof(buf));
1915+
err = SSL_get_error(ssl_handle, nread);
1916+
switch (err) {
1917+
case SSL_ERROR_NONE: /* this is not an error */
1918+
case SSL_ERROR_ZERO_RETURN: /* no more data */
1919+
/* This is the expected response. There was no data but only
1920+
the close notify alert */
1921+
done = 1;
1922+
break;
1923+
case SSL_ERROR_WANT_READ:
1924+
/* there's data pending, re-invoke SSL_read() */
1925+
break;
1926+
case SSL_ERROR_WANT_WRITE:
1927+
/* SSL wants a write. Really odd. Let's bail out. */
1928+
done = 1;
1929+
break;
1930+
default:
1931+
if ((sslerror = ERR_get_error())) {
1932+
ERR_error_string_n(sslerror, buf, sizeof(buf));
1933+
}
1934+
php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s (%d)", (sslerror ? buf : strerror(errno)), errno);
1935+
done = 1;
1936+
break;
1937+
}
1938+
}
1939+
}
1940+
(void)SSL_free(ssl_handle);
1941+
}
1942+
#endif
1943+
/* }}} */
1944+
18841945
/* {{{ data_close
18851946
*/
18861947
databuf_t*
@@ -1893,8 +1954,7 @@ data_close(ftpbuf_t *ftp, databuf_t *data)
18931954
#ifdef HAVE_FTP_SSL
18941955
if (data->ssl_active) {
18951956
/* don't free the data context, it's the same as the control */
1896-
SSL_shutdown(data->ssl_handle);
1897-
SSL_free(data->ssl_handle);
1957+
ftp_ssl_shutdown(ftp, data->listener, data->ssl_handle);
18981958
data->ssl_active = 0;
18991959
}
19001960
#endif
@@ -1904,8 +1964,7 @@ data_close(ftpbuf_t *ftp, databuf_t *data)
19041964
#ifdef HAVE_FTP_SSL
19051965
if (data->ssl_active) {
19061966
/* don't free the data context, it's the same as the control */
1907-
SSL_shutdown(data->ssl_handle);
1908-
SSL_free(data->ssl_handle);
1967+
ftp_ssl_shutdown(ftp, data->fd, data->ssl_handle);
19091968
data->ssl_active = 0;
19101969
}
19111970
#endif

0 commit comments

Comments
 (0)