[3.6] bpo-31400: Improve SSL error handling on Windows (GH-3463) (#3466) · python/cpython@16f16db
@@ -302,6 +302,11 @@ typedef struct {
302302enum py_ssl_server_or_client socket_type;
303303PyObject *owner; /* Python level "owner" passed to servername callback */
304304PyObject *server_hostname;
305+int ssl_errno; /* last seen error from SSL */
306+int c_errno; /* last seen error from libc */
307+#ifdef MS_WINDOWS
308+int ws_errno; /* last seen error from winsock */
309+#endif
305310} PySSLSocket;
306311307312typedef struct {
@@ -321,6 +326,20 @@ static PyTypeObject PySSLSocket_Type;
321326static PyTypeObject PySSLMemoryBIO_Type;
322327static PyTypeObject PySSLSession_Type;
323328329+#ifdef MS_WINDOWS
330+#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \
331+ (sock)->ws_errno = WSAGetLastError(); \
332+ (sock)->c_errno = errno; \
333+ (sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \
334+ } else { sock->ws_errno = 0; sock->c_errno = 0; sock->ssl_errno = 0; }
335+#else
336+#define _PySSL_UPDATE_ERRNO_IF(cond, sock, retcode) if (cond) { \
337+ (sock)->c_errno = errno; \
338+ (sock)->ssl_errno = SSL_get_error((sock->ssl), (retcode)); \
339+ } else { (sock)->c_errno = 0; (sock)->ssl_errno = 0; }
340+#endif
341+#define _PySSL_UPDATE_ERRNO(sock, retcode) _PySSL_UPDATE_ERRNO_IF(1, (sock), (retcode))
342+324343/*[clinic input]
325344module _ssl
326345class _ssl._SSLContext "PySSLContext *" "&PySSLContext_Type"
@@ -494,7 +513,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
494513e = ERR_peek_last_error();
495514496515if (obj->ssl != NULL) {
497-err = SSL_get_error(obj->ssl, ret);
516+err = obj->ssl_errno;
498517499518switch (err) {
500519case SSL_ERROR_ZERO_RETURN:
@@ -530,8 +549,16 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
530549errstr = "EOF occurred in violation of protocol";
531550 } else if (s && ret == -1) {
532551/* underlying BIO reported an I/O error */
533-Py_INCREF(s);
534552ERR_clear_error();
553+#ifdef MS_WINDOWS
554+if (obj->ws_errno)
555+return PyErr_SetFromWindowsErr(obj->ws_errno);
556+#endif
557+if (obj->c_errno) {
558+errno = obj->c_errno;
559+return PyErr_SetFromErrno(PyExc_OSError);
560+ }
561+Py_INCREF(s);
535562s->errorhandler();
536563Py_DECREF(s);
537564return NULL;
@@ -609,6 +636,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
609636 }
610637self->server_hostname = hostname;
611638 }
639+self->ssl_errno = 0;
640+self->c_errno = 0;
641+#ifdef MS_WINDOWS
642+self->ws_errno = 0;
643+#endif
612644613645/* Make sure the SSL error state is initialized */
614646 (void) ERR_get_state();
@@ -706,8 +738,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
706738do {
707739PySSL_BEGIN_ALLOW_THREADS
708740ret = SSL_do_handshake(self->ssl);
709-err = SSL_get_error(self->ssl, ret);
741+_PySSL_UPDATE_ERRNO_IF(ret < 1, self, ret);
710742PySSL_END_ALLOW_THREADS
743+err = self->ssl_errno;
711744712745if (PyErr_CheckSignals())
713746 goto error;
@@ -2003,8 +2036,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
20032036do {
20042037PySSL_BEGIN_ALLOW_THREADS
20052038len = SSL_write(self->ssl, b->buf, (int)b->len);
2006-err = SSL_get_error(self->ssl, len);
2039+_PySSL_UPDATE_ERRNO_IF(len <= 0, self, len);
20072040PySSL_END_ALLOW_THREADS
2041+err = self->ssl_errno;
2008204220092043if (PyErr_CheckSignals())
20102044 goto error;
@@ -2058,6 +2092,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
2058209220592093PySSL_BEGIN_ALLOW_THREADS
20602094count = SSL_pending(self->ssl);
2095+_PySSL_UPDATE_ERRNO_IF(count < 0, self, count);
20612096PySSL_END_ALLOW_THREADS
20622097if (count < 0)
20632098return PySSL_SetError(self, count, __FILE__, __LINE__);
@@ -2146,7 +2181,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
21462181do {
21472182PySSL_BEGIN_ALLOW_THREADS
21482183count = SSL_read(self->ssl, mem, len);
2149-err = SSL_get_error(self->ssl, count);
2184+_PySSL_UPDATE_ERRNO_IF(count <= 0, self, count);
21502185PySSL_END_ALLOW_THREADS
2151218621522187if (PyErr_CheckSignals())
@@ -2155,6 +2190,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
21552190if (has_timeout)
21562191timeout = deadline - _PyTime_GetMonotonicClock();
215721922193+err = self->ssl_errno;
21582194if (err == SSL_ERROR_WANT_READ) {
21592195sockstate = PySSL_select(sock, 0, timeout);
21602196 } else if (err == SSL_ERROR_WANT_WRITE) {
@@ -2211,7 +2247,7 @@ static PyObject *
22112247_ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
22122248/*[clinic end generated code: output=ca1aa7ed9d25ca42 input=ede2cc1a2ddf0ee4]*/
22132249{
2214-int err, ssl_err, sockstate, nonblocking;
2250+int err, sockstate, nonblocking;
22152251int zeros = 0;
22162252PySocketSockObject *sock = GET_SOCKET(self);
22172253_PyTime_t timeout, deadline = 0;
@@ -2250,6 +2286,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
22502286if (self->shutdown_seen_zero)
22512287SSL_set_read_ahead(self->ssl, 0);
22522288err = SSL_shutdown(self->ssl);
2289+_PySSL_UPDATE_ERRNO_IF(err < 0, self, err);
22532290PySSL_END_ALLOW_THREADS
2254229122552292/* If err == 1, a secure shutdown with SSL_shutdown() is complete */
@@ -2270,16 +2307,16 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
22702307timeout = deadline - _PyTime_GetMonotonicClock();
2271230822722309/* Possibly retry shutdown until timeout or failure */
2273-ssl_err = SSL_get_error(self->ssl, err);
2274-if (ssl_err == SSL_ERROR_WANT_READ)
2310+_PySSL_UPDATE_ERRNO(self, err);
2311+if (self->ssl_errno == SSL_ERROR_WANT_READ)
22752312sockstate = PySSL_select(sock, 0, timeout);
2276-else if (ssl_err == SSL_ERROR_WANT_WRITE)
2313+else if (self->ssl_errno == SSL_ERROR_WANT_WRITE)
22772314sockstate = PySSL_select(sock, 1, timeout);
22782315else
22792316break;
2280231722812318if (sockstate == SOCKET_HAS_TIMED_OUT) {
2282-if (ssl_err == SSL_ERROR_WANT_READ)
2319+if (self->ssl_errno == SSL_ERROR_WANT_READ)
22832320PyErr_SetString(PySocketModule.timeout_error,
22842321"The read operation timed out");
22852322else