diff options
author | Nikita Popov <nikita.ppv@gmail.com> | 2018-10-05 18:41:16 +0200 |
---|---|---|
committer | Nikita Popov <nikita.ppv@gmail.com> | 2018-10-05 18:41:28 +0200 |
commit | fde2ae3721b7e2b97a382347c06b307c6ad8874c (patch) | |
tree | f7e2cb76e8213ef79c7b15cd433b8fbfec0a43c6 /ext/ftp | |
parent | 42b769b9e237f7d24bc0ba4a953846ea6556932f (diff) | |
parent | bb4a2e8bb7fc80fa5b3725508bcc8fea525f059b (diff) | |
download | php-git-fde2ae3721b7e2b97a382347c06b307c6ad8874c.tar.gz |
Merge branch 'PHP-7.1' into PHP-7.2
Diffstat (limited to 'ext/ftp')
-rw-r--r-- | ext/ftp/ftp.c | 71 |
1 files changed, 65 insertions, 6 deletions
diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 6d03f86d17..9b409026a7 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -59,6 +59,7 @@ #ifdef HAVE_FTP_SSL #include <openssl/ssl.h> +#include <openssl/err.h> #endif #include "ftp.h" @@ -100,6 +101,11 @@ static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data); /* generic file lister */ static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, const char *path, const size_t path_len); +#ifdef HAVE_FTP_SSL +/* shuts down a TLS/SSL connection */ +static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle); +#endif + /* IP and port conversion box */ union ipbox { struct in_addr ia[2]; @@ -173,8 +179,7 @@ ftp_close(ftpbuf_t *ftp) if (ftp->fd != -1) { #ifdef HAVE_FTP_SSL if (ftp->ssl_active) { - SSL_shutdown(ftp->ssl_handle); - SSL_free(ftp->ssl_handle); + ftp_ssl_shutdown(ftp, ftp->fd, ftp->ssl_handle); } #endif closesocket(ftp->fd); @@ -1881,6 +1886,62 @@ data_accepted: } /* }}} */ +/* {{{ ftp_ssl_shutdown + */ +#ifdef HAVE_FTP_SSL +static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle) { + /* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train + the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the + server might get an ECONNRESET which might lead to data truncation on server side. + */ + char buf[256]; /* We will use this for the OpenSSL error buffer, so it has + to be at least 256 bytes long.*/ + int done = 1, err, nread; + unsigned long sslerror; + + err = SSL_shutdown(ssl_handle); + if (err < 0) { + php_error_docref(NULL, E_WARNING, "SSL_shutdown failed"); + } + else if (err == 0) { + /* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */ + done = 0; + } + + while (!done) { + if (data_available(ftp, fd)) { + ERR_clear_error(); + nread = SSL_read(ssl_handle, buf, sizeof(buf)); + err = SSL_get_error(ssl_handle, nread); + switch (err) { + case SSL_ERROR_NONE: /* this is not an error */ + case SSL_ERROR_ZERO_RETURN: /* no more data */ + /* This is the expected response. There was no data but only + the close notify alert */ + done = 1; + break; + case SSL_ERROR_WANT_READ: + /* there's data pending, re-invoke SSL_read() */ + break; + case SSL_ERROR_WANT_WRITE: + /* SSL wants a write. Really odd. Let's bail out. */ + done = 1; + break; + default: + if ((sslerror = ERR_get_error())) { + ERR_error_string_n(sslerror, buf, sizeof(buf)); + } + php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s (%d)", (sslerror ? buf : strerror(errno)), errno); + done = 1; + break; + } + } + } + (void)SSL_free(ssl_handle); +} +#endif +/* }}} */ + /* {{{ data_close */ databuf_t* @@ -1893,8 +1954,7 @@ data_close(ftpbuf_t *ftp, databuf_t *data) #ifdef HAVE_FTP_SSL if (data->ssl_active) { /* don't free the data context, it's the same as the control */ - SSL_shutdown(data->ssl_handle); - SSL_free(data->ssl_handle); + ftp_ssl_shutdown(ftp, data->listener, data->ssl_handle); data->ssl_active = 0; } #endif @@ -1904,8 +1964,7 @@ data_close(ftpbuf_t *ftp, databuf_t *data) #ifdef HAVE_FTP_SSL if (data->ssl_active) { /* don't free the data context, it's the same as the control */ - SSL_shutdown(data->ssl_handle); - SSL_free(data->ssl_handle); + ftp_ssl_shutdown(ftp, data->fd, data->ssl_handle); data->ssl_active = 0; } #endif |