[3.6] bpo-31400: Improve SSL error handling on Windows (GH-3463) (#3466) · python/cpython@16f16db

@@ -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;

306311307312

typedef struct {

@@ -321,6 +326,20 @@ static PyTypeObject PySSLSocket_Type;

321326

static PyTypeObject PySSLMemoryBIO_Type;

322327

static 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]

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();

495514496515

if (obj->ssl != NULL) {

497-

err = SSL_get_error(obj->ssl, ret);

516+

err = obj->ssl_errno;

498517499518

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

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)

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;

711744712745

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;

2008204220092043

if (PyErr_CheckSignals())

20102044

goto error;

@@ -2058,6 +2092,7 @@ _ssl__SSLSocket_pending_impl(PySSLSocket *self)

2058209220592093

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

2151218621522187

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();

215721922193+

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

2254229122552292

/* 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();

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)

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;

2280231722812318

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