diff options
author | Christian Heimes <christian@python.org> | 2017-09-09 15:22:11 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-09-09 15:22:11 (GMT) |
commit | 16f16dbd0ed02cd1a7b270eb6dd80d9bd179902e (patch) | |
tree | 06c750dd445febb2ae8a6cfee323ef822b0acb19 /Modules | |
parent | a4baf1c543bca261c27e98ba296e42665f3cb872 (diff) | |
download | cpython-16f16dbd0ed02cd1a7b270eb6dd80d9bd179902e.zip cpython-16f16dbd0ed02cd1a7b270eb6dd80d9bd179902e.tar.gz cpython-16f16dbd0ed02cd1a7b270eb6dd80d9bd179902e.tar.bz2 |
[3.6] bpo-31400: Improve SSL error handling on Windows (GH-3463) (#3466)
* bpo-31392: Improve SSL error handling on Windows
* Remove unnecessary Windows mention in NEWS.
(cherry picked from commit e6eb48c10dc389d1d70657593de6a6cb3087d3d1)
Diffstat (limited to 'Modules')
-rw-r--r-- | Modules/_ssl.c | 57 |
1 files changed, 47 insertions, 10 deletions
diff --git a/Modules/_ssl.c b/Modules/_ssl.c index a28a6e0..4a656a1 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -302,6 +302,11 @@ typedef struct { enum py_ssl_server_or_client socket_type; PyObject *owner; /* Python level "owner" passed to servername callback */ PyObject *server_hostname; + int ssl_errno; /* last seen error from SSL */ + int c_errno; /* last seen error from libc */ +#ifdef MS_WINDOWS + int ws_errno; /* last seen error from winsock */ +#endif } PySSLSocket; typedef struct { @@ -321,6 +326,20 @@ static PyTypeObject PySSLSocket_Type; static PyTypeObject PySSLMemoryBIO_Type; static PyTypeObject PySSLSession_Type; +#ifdef MS_WINDOWS +#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \ + (sock)->ws_errno = WSAGetLastError(); \ + (sock)->c_errno = errno; \ + (sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \ + } else { sock->ws_errno = 0; sock->c_errno = 0; sock->ssl_errno = 0; } +#else +#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \ + (sock)->c_errno = errno; \ + (sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \ + } else { (sock)->c_errno = 0; (sock)->ssl_errno = 0; } +#endif +#define _PySSL_UPDATE_ERRNO(sock, retcode) _PySSL_UPDATE_ERRNO_IF(1, (sock), (retcode)) + /*[clinic input] module _ssl class _ssl._SSLContext "PySSLContext *" "&PySSLContext_Type" @@ -494,7 +513,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno) e = ERR_peek_last_error(); if (obj->ssl != NULL) { - err = SSL_get_error(obj->ssl, ret); + err = obj->ssl_errno; switch (err) { case SSL_ERROR_ZERO_RETURN: @@ -530,8 +549,16 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno) errstr = "EOF occurred in violation of protocol"; } else if (s && ret == -1) { /* underlying BIO reported an I/O error */ - Py_INCREF(s); ERR_clear_error(); +#ifdef MS_WINDOWS + if (obj->ws_errno) + return PyErr_SetFromWindowsErr(obj->ws_errno); +#endif + if (obj->c_errno) { + errno = obj->c_errno; + return PyErr_SetFromErrno(PyExc_OSError); + } + Py_INCREF(s); s->errorhandler(); Py_DECREF(s); return NULL; @@ -609,6 +636,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, } self->server_hostname = hostname; } + self->ssl_errno = 0; + self->c_errno = 0; +#ifdef MS_WINDOWS + self->ws_errno = 0; +#endif /* Make sure the SSL error state is initialized */ (void) ERR_get_state(); @@ -706,8 +738,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self) do { PySSL_BEGIN_ALLOW_THREADS ret = SSL_do_handshake(self->ssl); - err = SSL_get_error(self->ssl, ret); + _PySSL_UPDATE_ERRNO_IF(ret < 1, self, ret); PySSL_END_ALLOW_THREADS + err = self->ssl_errno; if (PyErr_CheckSignals()) goto error; @@ -2003,8 +2036,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b) do { PySSL_BEGIN_ALLOW_THREADS len = SSL_write(self->ssl, b->buf, (int)b->len); - err = SSL_get_error(self->ssl, len); + _PySSL_UPDATE_ERRNO_IF(len <= 0, self, len); PySSL_END_ALLOW_THREADS + err = self->ssl_errno; if (PyErr_CheckSignals()) goto error; @@ -2058,6 +2092,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self) PySSL_BEGIN_ALLOW_THREADS count = SSL_pending(self->ssl); + _PySSL_UPDATE_ERRNO_IF(count < 0, self, count); PySSL_END_ALLOW_THREADS if (count < 0) return PySSL_SetError(self, count, __FILE__, __LINE__); @@ -2146,7 +2181,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1, do { PySSL_BEGIN_ALLOW_THREADS count = SSL_read(self->ssl, mem, len); - err = SSL_get_error(self->ssl, count); + _PySSL_UPDATE_ERRNO_IF(count <= 0, self, count); PySSL_END_ALLOW_THREADS if (PyErr_CheckSignals()) @@ -2155,6 +2190,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1, if (has_timeout) timeout = deadline - _PyTime_GetMonotonicClock(); + err = self->ssl_errno; if (err == SSL_ERROR_WANT_READ) { sockstate = PySSL_select(sock, 0, timeout); } else if (err == SSL_ERROR_WANT_WRITE) { @@ -2211,7 +2247,7 @@ static PyObject * _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) /*[clinic end generated code: output=ca1aa7ed9d25ca42 input=ede2cc1a2ddf0ee4]*/ { - int err, ssl_err, sockstate, nonblocking; + int err, sockstate, nonblocking; int zeros = 0; PySocketSockObject *sock = GET_SOCKET(self); _PyTime_t timeout, deadline = 0; @@ -2250,6 +2286,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) if (self->shutdown_seen_zero) SSL_set_read_ahead(self->ssl, 0); err = SSL_shutdown(self->ssl); + _PySSL_UPDATE_ERRNO_IF(err < 0, self, err); PySSL_END_ALLOW_THREADS /* If err == 1, a secure shutdown with SSL_shutdown() is complete */ @@ -2270,16 +2307,16 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self) timeout = deadline - _PyTime_GetMonotonicClock(); /* Possibly retry shutdown until timeout or failure */ - ssl_err = SSL_get_error(self->ssl, err); - if (ssl_err == SSL_ERROR_WANT_READ) + _PySSL_UPDATE_ERRNO(self, err); + if (self->ssl_errno == SSL_ERROR_WANT_READ) sockstate = PySSL_select(sock, 0, timeout); - else if (ssl_err == SSL_ERROR_WANT_WRITE) + else if (self->ssl_errno == SSL_ERROR_WANT_WRITE) sockstate = PySSL_select(sock, 1, timeout); else break; if (sockstate == SOCKET_HAS_TIMED_OUT) { - if (ssl_err == SSL_ERROR_WANT_READ) + if (self->ssl_errno == SSL_ERROR_WANT_READ) PyErr_SetString(PySocketModule.timeout_error, "The read operation timed out"); else |