Skip to content

Commit 16f16db

Browse files
authored
[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 e6eb48c)
1 parent a4baf1c commit 16f16db

File tree

2 files changed

+48
-10
lines changed

2 files changed

+48
-10
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Improves SSL error handling to avoid losing error numbers.

‎Modules/_ssl.c‎

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,11 @@ typedef struct {
302302
enum py_ssl_server_or_client socket_type;
303303
PyObject *owner; /* Python level "owner" passed to servername callback */
304304
PyObject *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;
306311

307312
typedef struct {
@@ -321,6 +326,20 @@ static PyTypeObject PySSLSocket_Type;
321326
static PyTypeObject PySSLMemoryBIO_Type;
322327
static PyTypeObject PySSLSession_Type;
323328

329+
#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]
325344
module _ssl
326345
class _ssl._SSLContext "PySSLContext *" "&PySSLContext_Type"
@@ -494,7 +513,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
494513
e = ERR_peek_last_error();
495514

496515
if (obj->ssl != NULL) {
497-
err = SSL_get_error(obj->ssl, ret);
516+
err = obj->ssl_errno;
498517

499518
switch (err) {
500519
case SSL_ERROR_ZERO_RETURN:
@@ -530,8 +549,16 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
530549
errstr = "EOF occurred in violation of protocol";
531550
} else if (s && ret == -1) {
532551
/* underlying BIO reported an I/O error */
533-
Py_INCREF(s);
534552
ERR_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);
535562
s->errorhandler();
536563
Py_DECREF(s);
537564
return NULL;
@@ -609,6 +636,11 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
609636
}
610637
self->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
612644

613645
/* Make sure the SSL error state is initialized */
614646
(void) ERR_get_state();
@@ -706,8 +738,9 @@ _ssl__SSLSocket_do_handshake_impl(PySSLSocket *self)
706738
do {
707739
PySSL_BEGIN_ALLOW_THREADS
708740
ret = SSL_do_handshake(self->ssl);
709-
err = SSL_get_error(self->ssl, ret);
741+
_PySSL_UPDATE_ERRNO_IF(ret < 1, self, ret);
710742
PySSL_END_ALLOW_THREADS
743+
err = self->ssl_errno;
711744

712745
if (PyErr_CheckSignals())
713746
goto error;
@@ -2003,8 +2036,9 @@ _ssl__SSLSocket_write_impl(PySSLSocket *self, Py_buffer *b)
20032036
do {
20042037
PySSL_BEGIN_ALLOW_THREADS
20052038
len = 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);
20072040
PySSL_END_ALLOW_THREADS
2041+
err = self->ssl_errno;
20082042

20092043
if (PyErr_CheckSignals())
20102044
goto error;
@@ -2058,6 +2092,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)
20582092

20592093
PySSL_BEGIN_ALLOW_THREADS
20602094
count = SSL_pending(self->ssl);
2095+
_PySSL_UPDATE_ERRNO_IF(count < 0, self, count);
20612096
PySSL_END_ALLOW_THREADS
20622097
if (count < 0)
20632098
return PySSL_SetError(self, count, __FILE__, __LINE__);
@@ -2146,7 +2181,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
21462181
do {
21472182
PySSL_BEGIN_ALLOW_THREADS
21482183
count = SSL_read(self->ssl, mem, len);
2149-
err = SSL_get_error(self->ssl, count);
2184+
_PySSL_UPDATE_ERRNO_IF(count <= 0, self, count);
21502185
PySSL_END_ALLOW_THREADS
21512186

21522187
if (PyErr_CheckSignals())
@@ -2155,6 +2190,7 @@ _ssl__SSLSocket_read_impl(PySSLSocket *self, int len, int group_right_1,
21552190
if (has_timeout)
21562191
timeout = deadline - _PyTime_GetMonotonicClock();
21572192

2193+
err = self->ssl_errno;
21582194
if (err == SSL_ERROR_WANT_READ) {
21592195
sockstate = 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;
22152251
int zeros = 0;
22162252
PySocketSockObject *sock = GET_SOCKET(self);
22172253
_PyTime_t timeout, deadline = 0;
@@ -2250,6 +2286,7 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
22502286
if (self->shutdown_seen_zero)
22512287
SSL_set_read_ahead(self->ssl, 0);
22522288
err = SSL_shutdown(self->ssl);
2289+
_PySSL_UPDATE_ERRNO_IF(err < 0, self, err);
22532290
PySSL_END_ALLOW_THREADS
22542291

22552292
/* If err == 1, a secure shutdown with SSL_shutdown() is complete */
@@ -2270,16 +2307,16 @@ _ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
22702307
timeout = deadline - _PyTime_GetMonotonicClock();
22712308

22722309
/* 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)
22752312
sockstate = PySSL_select(sock, 0, timeout);
2276-
else if (ssl_err == SSL_ERROR_WANT_WRITE)
2313+
else if (self->ssl_errno == SSL_ERROR_WANT_WRITE)
22772314
sockstate = PySSL_select(sock, 1, timeout);
22782315
else
22792316
break;
22802317

22812318
if (sockstate == SOCKET_HAS_TIMED_OUT) {
2282-
if (ssl_err == SSL_ERROR_WANT_READ)
2319+
if (self->ssl_errno == SSL_ERROR_WANT_READ)
22832320
PyErr_SetString(PySocketModule.timeout_error,
22842321
"The read operation timed out");
22852322
else

0 commit comments

Comments
 (0)