67
67
68
68
#ifdef HAVE_FTP_SSL
69
69
#include <openssl/ssl.h>
70
+ #include <openssl/err.h>
70
71
#endif
71
72
72
73
#include "ftp.h"
@@ -111,6 +112,11 @@ static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
111
112
/* generic file lister */
112
113
static char * * ftp_genlist (ftpbuf_t * ftp , const char * cmd , const char * path );
113
114
115
+ #ifdef HAVE_FTP_SSL
116
+ /* shuts down a TLS/SSL connection */
117
+ static void ftp_ssl_shutdown (ftpbuf_t * ftp , php_socket_t fd , SSL * ssl_handle );
118
+ #endif
119
+
114
120
/* IP and port conversion box */
115
121
union ipbox {
116
122
struct in_addr ia [2 ];
@@ -184,8 +190,7 @@ ftp_close(ftpbuf_t *ftp)
184
190
if (ftp -> fd != -1 ) {
185
191
#ifdef HAVE_FTP_SSL
186
192
if (ftp -> ssl_active ) {
187
- SSL_shutdown (ftp -> ssl_handle );
188
- SSL_free (ftp -> ssl_handle );
193
+ ftp_ssl_shutdown (ftp , ftp -> fd , ftp -> ssl_handle );
189
194
}
190
195
#endif
191
196
closesocket (ftp -> fd );
@@ -1743,6 +1748,62 @@ data_accept(databuf_t *data, ftpbuf_t *ftp)
1743
1748
}
1744
1749
/* }}} */
1745
1750
1751
+ /* {{{ ftp_ssl_shutdown
1752
+ */
1753
+ #ifdef HAVE_FTP_SSL
1754
+ static void ftp_ssl_shutdown (ftpbuf_t * ftp , php_socket_t fd , SSL * ssl_handle ) {
1755
+ /* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train
1756
+ the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the
1757
+ server might get an ECONNRESET which might lead to data truncation on server side.
1758
+ */
1759
+ char buf [256 ]; /* We will use this for the OpenSSL error buffer, so it has
1760
+ to be at least 256 bytes long.*/
1761
+ int done = 1 , err , nread ;
1762
+ unsigned long sslerror ;
1763
+
1764
+ err = SSL_shutdown (ssl_handle );
1765
+ if (err < 0 ) {
1766
+ php_error_docref (NULL , E_WARNING , "SSL_shutdown failed" );
1767
+ }
1768
+ else if (err == 0 ) {
1769
+ /* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */
1770
+ done = 0 ;
1771
+ }
1772
+
1773
+ while (!done ) {
1774
+ if (data_available (ftp , fd )) {
1775
+ ERR_clear_error ();
1776
+ nread = SSL_read (ssl_handle , buf , sizeof (buf ));
1777
+ err = SSL_get_error (ssl_handle , nread );
1778
+ switch (err ) {
1779
+ case SSL_ERROR_NONE : /* this is not an error */
1780
+ case SSL_ERROR_ZERO_RETURN : /* no more data */
1781
+ /* This is the expected response. There was no data but only
1782
+ the close notify alert */
1783
+ done = 1 ;
1784
+ break ;
1785
+ case SSL_ERROR_WANT_READ :
1786
+ /* there's data pending, re-invoke SSL_read() */
1787
+ break ;
1788
+ case SSL_ERROR_WANT_WRITE :
1789
+ /* SSL wants a write. Really odd. Let's bail out. */
1790
+ done = 1 ;
1791
+ break ;
1792
+ default :
1793
+ if ((sslerror = ERR_get_error ())) {
1794
+ ERR_error_string_n (sslerror , buf , sizeof (buf ));
1795
+ }
1796
+ php_error_docref (NULL , E_WARNING , "SSL_read on shutdown: %s (%d)" , (sslerror ? buf : strerror (errno )), errno );
1797
+ done = 1 ;
1798
+ break ;
1799
+ }
1800
+ }
1801
+ }
1802
+ (void )SSL_free (ssl_handle );
1803
+ }
1804
+ #endif
1805
+ /* }}} */
1806
+
1746
1807
/* {{{ data_close
1747
1808
*/
1748
1809
databuf_t *
@@ -1758,8 +1819,7 @@ data_close(ftpbuf_t *ftp, databuf_t *data)
1758
1819
#ifdef HAVE_FTP_SSL
1759
1820
if (data -> ssl_active ) {
1760
1821
/* don't free the data context, it's the same as the control */
1761
- SSL_shutdown (data -> ssl_handle );
1762
- SSL_free (data -> ssl_handle );
1822
+ ftp_ssl_shutdown (ftp , data -> listener , data -> ssl_handle );
1763
1823
data -> ssl_active = 0 ;
1764
1824
}
1765
1825
#endif
@@ -1769,8 +1829,7 @@ data_close(ftpbuf_t *ftp, databuf_t *data)
1769
1829
#ifdef HAVE_FTP_SSL
1770
1830
if (data -> ssl_active ) {
1771
1831
/* don't free the data context, it's the same as the control */
1772
- SSL_shutdown (data -> ssl_handle );
1773
- SSL_free (data -> ssl_handle );
1832
+ ftp_ssl_shutdown (ftp , data -> fd , data -> ssl_handle );
1774
1833
data -> ssl_active = 0 ;
1775
1834
}
1776
1835
#endif
0 commit comments