diff -r 18825546acbc Lib/ssl.py --- a/Lib/ssl.py Sun Sep 18 14:34:13 2016 +0200 +++ b/Lib/ssl.py Sun Sep 18 20:49:00 2016 +0200 @@ -104,7 +104,7 @@ from _ssl import _SSLContext, MemoryBIO, SSLSession from _ssl import ( SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, - SSLSyscallError, SSLEOFError, + SSLSyscallError, SSLEOFError, SSLCertVerificationError ) from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_add, RAND_bytes, RAND_pseudo_bytes diff -r 18825546acbc Lib/test/test_ssl.py --- a/Lib/test/test_ssl.py Sun Sep 18 14:34:13 2016 +0200 +++ b/Lib/test/test_ssl.py Sun Sep 18 20:49:00 2016 +0200 @@ -1489,8 +1489,10 @@ s = test_wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED) self.addCleanup(s.close) - self.assertRaisesRegex(ssl.SSLError, "certificate verify failed", - s.connect, self.server_addr) + self.assertRaisesRegex( + ssl.SSLCertVerificationError, + "certificate verify failed: unable to get local issuer certificate", + s.connect, self.server_addr) def test_connect_ex(self): # Issue #11326: check connect_ex() implementation @@ -1553,8 +1555,10 @@ ctx.verify_mode = ssl.CERT_REQUIRED s = ctx.wrap_socket(socket.socket(socket.AF_INET)) self.addCleanup(s.close) - self.assertRaisesRegex(ssl.SSLError, "certificate verify failed", - s.connect, self.server_addr) + self.assertRaisesRegex( + ssl.SSLCertVerificationError, + "certificate verify failed: unable to get local issuer certificate", + s.connect, self.server_addr) def test_connect_capath(self): # Verify server certificates using the `capath` argument @@ -2405,8 +2409,9 @@ server = ThreadedEchoServer(context=server_context, chatty=True) with server: with context.wrap_socket(socket.socket()) as s: - with self.assertRaisesRegex(ssl.SSLError, - "certificate verify failed"): + with self.assertRaisesRegex( + ssl.SSLCertVerificationError, + "certificate verify failed: unable to get certificate CRL"): s.connect((HOST, server.port)) # now load a CRL file. The CRL file is signed by the CA. diff -r 18825546acbc Modules/_ssl.c --- a/Modules/_ssl.c Sun Sep 18 14:34:13 2016 +0200 +++ b/Modules/_ssl.c Sun Sep 18 20:49:00 2016 +0200 @@ -76,6 +76,7 @@ /* SSL error object */ static PyObject *PySSLErrorObject; +static PyObject *PySSLCertVerificationErrorObject; static PyObject *PySSLZeroReturnErrorObject; static PyObject *PySSLWantReadErrorObject; static PyObject *PySSLWantWriteErrorObject; @@ -368,6 +369,9 @@ PyDoc_STRVAR(SSLError_doc, "An error occurred in the SSL implementation."); +PyDoc_STRVAR(SSLCertVerificationError_doc, +"A certificate could not be verified."); + PyDoc_STRVAR(SSLZeroReturnError_doc, "SSL/TLS session closed cleanly."); @@ -413,9 +417,10 @@ static void fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr, - int lineno, unsigned long errcode) + int lineno, unsigned long errcode, long verify_code) { PyObject *err_value = NULL, *reason_obj = NULL, *lib_obj = NULL; + const char *verify_str = NULL; PyObject *init_value, *msg, *key; _Py_IDENTIFIER(reason); _Py_IDENTIFIER(library); @@ -447,15 +452,23 @@ } if (errstr == NULL) errstr = "unknown error"; - - if (reason_obj && lib_obj) + if (verify_code != -1) { + verify_str = X509_verify_cert_error_string(verify_code); + } + + if (verify_str && reason_obj && lib_obj) { + msg = PyUnicode_FromFormat("[%S: %S] %s: %s (_ssl.c:%d)", + lib_obj, reason_obj, errstr, verify_str, + lineno); + } else if (reason_obj && lib_obj) { msg = PyUnicode_FromFormat("[%S: %S] %s (_ssl.c:%d)", lib_obj, reason_obj, errstr, lineno); - else if (lib_obj) + } else if (lib_obj) { msg = PyUnicode_FromFormat("[%S] %s (_ssl.c:%d)", lib_obj, errstr, lineno); - else + } else { msg = PyUnicode_FromFormat("%s (_ssl.c:%d)", errstr, lineno); + } if (msg == NULL) goto fail; @@ -487,6 +500,7 @@ PyObject *type = PySSLErrorObject; char *errstr = NULL; int err; + long verify_code = -1; enum py_ssl_error p = PY_SSL_ERROR_NONE; unsigned long e = 0; @@ -551,6 +565,11 @@ if (e == 0) /* possible? */ errstr = "A failure in the SSL library occurred"; + if (ERR_GET_LIB(e) == ERR_LIB_SSL && + ERR_GET_REASON(e) == SSL_R_CERTIFICATE_VERIFY_FAILED) { + type = PySSLCertVerificationErrorObject; + verify_code = SSL_get_verify_result(obj->ssl); + } break; } default: @@ -558,7 +577,7 @@ errstr = "Invalid error code"; } } - fill_and_set_sslerror(type, p, errstr, lineno, e); + fill_and_set_sslerror(type, p, errstr, lineno, e, verify_code); ERR_clear_error(); return NULL; } @@ -570,7 +589,7 @@ errcode = ERR_peek_last_error(); else errcode = 0; - fill_and_set_sslerror(PySSLErrorObject, errcode, errstr, lineno, errcode); + fill_and_set_sslerror(PySSLErrorObject, errcode, errstr, lineno, errcode, -1); ERR_clear_error(); return NULL; } @@ -5190,6 +5209,9 @@ if (PySSLErrorObject == NULL) return NULL; + PySSLCertVerificationErrorObject = PyErr_NewExceptionWithDoc( + "ssl.SSLCertVerificationError", SSLCertVerificationError_doc, + PySSLErrorObject, NULL); PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc( "ssl.SSLZeroReturnError", SSLZeroReturnError_doc, PySSLErrorObject, NULL); @@ -5205,13 +5227,16 @@ PySSLEOFErrorObject = PyErr_NewExceptionWithDoc( "ssl.SSLEOFError", SSLEOFError_doc, PySSLErrorObject, NULL); - if (PySSLZeroReturnErrorObject == NULL + if (PySSLCertVerificationErrorObject == NULL + || PySSLZeroReturnErrorObject == NULL || PySSLWantReadErrorObject == NULL || PySSLWantWriteErrorObject == NULL || PySSLSyscallErrorObject == NULL || PySSLEOFErrorObject == NULL) return NULL; if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0 + || PyDict_SetItemString(d, "SSLCertVerificationError", + PySSLCertVerificationErrorObject) != 0 || PyDict_SetItemString(d, "SSLZeroReturnError", PySSLZeroReturnErrorObject) != 0 || PyDict_SetItemString(d, "SSLWantReadError", PySSLWantReadErrorObject) != 0 || PyDict_SetItemString(d, "SSLWantWriteError", PySSLWantWriteErrorObject) != 0