@@ -66,6 +66,7 @@ static PySocketModule_APIObject PySocketModule;
6666
6767/* SSL error object */
6868static PyObject * PySSLErrorObject ;
69+ static PyObject * PySSLCertVerificationErrorObject ;
6970static PyObject * PySSLZeroReturnErrorObject ;
7071static PyObject * PySSLWantReadErrorObject ;
7172static PyObject * PySSLWantWriteErrorObject ;
@@ -386,6 +387,9 @@ typedef enum {
386387PyDoc_STRVAR (SSLError_doc ,
387388"An error occurred in the SSL implementation." );
388389
390+ PyDoc_STRVAR (SSLCertVerificationError_doc ,
391+ "A certificate could not be verified." );
392+
389393PyDoc_STRVAR (SSLZeroReturnError_doc ,
390394"SSL/TLS session closed cleanly." );
391395
@@ -430,13 +434,16 @@ static PyType_Spec sslerror_type_spec = {
430434};
431435
432436static void
433- fill_and_set_sslerror (PyObject * type , int ssl_errno , const char * errstr ,
434- int lineno , unsigned long errcode )
437+ fill_and_set_sslerror (PySSLSocket * sslsock , PyObject * type , int ssl_errno ,
438+ const char * errstr , int lineno , unsigned long errcode )
435439{
436440 PyObject * err_value = NULL , * reason_obj = NULL , * lib_obj = NULL ;
441+ PyObject * verify_obj = NULL , * verify_code_obj = NULL ;
437442 PyObject * init_value , * msg , * key ;
438443 _Py_IDENTIFIER (reason );
439444 _Py_IDENTIFIER (library );
445+ _Py_IDENTIFIER (verify_message );
446+ _Py_IDENTIFIER (verify_code );
440447
441448 if (errcode != 0 ) {
442449 int lib , reason ;
@@ -466,7 +473,50 @@ fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr,
466473 if (errstr == NULL )
467474 errstr = "unknown error" ;
468475
469- if (reason_obj && lib_obj )
476+ /* verify code for cert validation error */
477+ if ((sslsock != NULL ) && (type == PySSLCertVerificationErrorObject )) {
478+ const char * verify_str = NULL ;
479+ long verify_code ;
480+
481+ verify_code = SSL_get_verify_result (sslsock -> ssl );
482+ verify_code_obj = PyLong_FromLong (verify_code );
483+ if (verify_code_obj == NULL ) {
484+ goto fail ;
485+ }
486+
487+ switch (verify_code ) {
488+ case X509_V_ERR_HOSTNAME_MISMATCH :
489+ verify_obj = PyUnicode_FromFormat (
490+ "Hostname mismatch, certificate is not valid for '%S'." ,
491+ sslsock -> server_hostname
492+ );
493+ break ;
494+ case X509_V_ERR_IP_ADDRESS_MISMATCH :
495+ verify_obj = PyUnicode_FromFormat (
496+ "IP address mismatch, certificate is not valid for '%S'." ,
497+ sslsock -> server_hostname
498+ );
499+ break ;
500+ default :
501+ verify_str = X509_verify_cert_error_string (verify_code );
502+ if (verify_str != NULL ) {
503+ verify_obj = PyUnicode_FromString (verify_str );
504+ } else {
505+ verify_obj = Py_None ;
506+ Py_INCREF (verify_obj );
507+ }
508+ break ;
509+ }
510+ if (verify_obj == NULL ) {
511+ goto fail ;
512+ }
513+ }
514+
515+ if (verify_obj && reason_obj && lib_obj )
516+ msg = PyUnicode_FromFormat ("[%S: %S] %s: %S (_ssl.c:%d)" ,
517+ lib_obj , reason_obj , errstr , verify_obj ,
518+ lineno );
519+ else if (reason_obj && lib_obj )
470520 msg = PyUnicode_FromFormat ("[%S: %S] %s (_ssl.c:%d)" ,
471521 lib_obj , reason_obj , errstr , lineno );
472522 else if (lib_obj )
@@ -490,17 +540,30 @@ fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr,
490540 reason_obj = Py_None ;
491541 if (_PyObject_SetAttrId (err_value , & PyId_reason , reason_obj ))
492542 goto fail ;
543+
493544 if (lib_obj == NULL )
494545 lib_obj = Py_None ;
495546 if (_PyObject_SetAttrId (err_value , & PyId_library , lib_obj ))
496547 goto fail ;
548+
549+ if ((sslsock != NULL ) && (type == PySSLCertVerificationErrorObject )) {
550+ /* Only set verify code / message for SSLCertVerificationError */
551+ if (_PyObject_SetAttrId (err_value , & PyId_verify_code ,
552+ verify_code_obj ))
553+ goto fail ;
554+ if (_PyObject_SetAttrId (err_value , & PyId_verify_message , verify_obj ))
555+ goto fail ;
556+ }
557+
497558 PyErr_SetObject (type , err_value );
498559fail :
499560 Py_XDECREF (err_value );
561+ Py_XDECREF (verify_code_obj );
562+ Py_XDECREF (verify_obj );
500563}
501564
502565static PyObject *
503- PySSL_SetError (PySSLSocket * obj , int ret , const char * filename , int lineno )
566+ PySSL_SetError (PySSLSocket * sslsock , int ret , const char * filename , int lineno )
504567{
505568 PyObject * type = PySSLErrorObject ;
506569 char * errstr = NULL ;
@@ -511,8 +574,8 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
511574 assert (ret <= 0 );
512575 e = ERR_peek_last_error ();
513576
514- if (obj -> ssl != NULL ) {
515- err = SSL_get_error (obj -> ssl , ret );
577+ if (sslsock -> ssl != NULL ) {
578+ err = SSL_get_error (sslsock -> ssl , ret );
516579
517580 switch (err ) {
518581 case SSL_ERROR_ZERO_RETURN :
@@ -541,7 +604,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
541604 case SSL_ERROR_SYSCALL :
542605 {
543606 if (e == 0 ) {
544- PySocketSockObject * s = GET_SOCKET (obj );
607+ PySocketSockObject * s = GET_SOCKET (sslsock );
545608 if (ret == 0 || (((PyObject * )s ) == Py_None )) {
546609 p = PY_SSL_ERROR_EOF ;
547610 type = PySSLEOFErrorObject ;
@@ -566,17 +629,22 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
566629 case SSL_ERROR_SSL :
567630 {
568631 p = PY_SSL_ERROR_SSL ;
569- if (e == 0 )
632+ if (e == 0 ) {
570633 /* possible? */
571634 errstr = "A failure in the SSL library occurred" ;
635+ }
636+ if (ERR_GET_LIB (e ) == ERR_LIB_SSL &&
637+ ERR_GET_REASON (e ) == SSL_R_CERTIFICATE_VERIFY_FAILED ) {
638+ type = PySSLCertVerificationErrorObject ;
639+ }
572640 break ;
573641 }
574642 default :
575643 p = PY_SSL_ERROR_INVALID_ERROR_CODE ;
576644 errstr = "Invalid error code" ;
577645 }
578646 }
579- fill_and_set_sslerror (type , p , errstr , lineno , e );
647+ fill_and_set_sslerror (sslsock , type , p , errstr , lineno , e );
580648 ERR_clear_error ();
581649 return NULL ;
582650}
@@ -588,15 +656,11 @@ _setSSLError (const char *errstr, int errcode, const char *filename, int lineno)
588656 errcode = ERR_peek_last_error ();
589657 else
590658 errcode = 0 ;
591- fill_and_set_sslerror (PySSLErrorObject , errcode , errstr , lineno , errcode );
659+ fill_and_set_sslerror (NULL , PySSLErrorObject , errcode , errstr , lineno , errcode );
592660 ERR_clear_error ();
593661 return NULL ;
594662}
595663
596- /*
597- * SSL objects
598- */
599-
600664static PySSLSocket *
601665newPySSLSocket (PySSLContext * sslctx , PySocketSockObject * sock ,
602666 enum py_ssl_server_or_client socket_type ,
@@ -656,7 +720,6 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
656720 if (server_hostname != NULL )
657721 SSL_set_tlsext_host_name (self -> ssl , server_hostname );
658722#endif
659-
660723 /* If the socket is in non-blocking mode or timeout mode, set the BIO
661724 * to non-blocking mode (blocking is the default)
662725 */
@@ -5130,7 +5193,7 @@ parse_openssl_version(unsigned long libver,
51305193PyMODINIT_FUNC
51315194PyInit__ssl (void )
51325195{
5133- PyObject * m , * d , * r ;
5196+ PyObject * m , * d , * r , * bases ;
51345197 unsigned long libver ;
51355198 unsigned int major , minor , fix , patch , status ;
51365199 PySocketModule_APIObject * socket_api ;
@@ -5182,6 +5245,14 @@ PyInit__ssl(void)
51825245 if (PySSLErrorObject == NULL )
51835246 return NULL ;
51845247
5248+ /* ssl.CertificateError used to be a subclass of ValueError */
5249+ bases = Py_BuildValue ("OO" , PySSLErrorObject , PyExc_ValueError );
5250+ if (bases == NULL )
5251+ return NULL ;
5252+ PySSLCertVerificationErrorObject = PyErr_NewExceptionWithDoc (
5253+ "ssl.SSLCertVerificationError" , SSLCertVerificationError_doc ,
5254+ bases , NULL );
5255+ Py_DECREF (bases );
51855256 PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc (
51865257 "ssl.SSLZeroReturnError" , SSLZeroReturnError_doc ,
51875258 PySSLErrorObject , NULL );
@@ -5197,13 +5268,16 @@ PyInit__ssl(void)
51975268 PySSLEOFErrorObject = PyErr_NewExceptionWithDoc (
51985269 "ssl.SSLEOFError" , SSLEOFError_doc ,
51995270 PySSLErrorObject , NULL );
5200- if (PySSLZeroReturnErrorObject == NULL
5271+ if (PySSLCertVerificationErrorObject == NULL
5272+ || PySSLZeroReturnErrorObject == NULL
52015273 || PySSLWantReadErrorObject == NULL
52025274 || PySSLWantWriteErrorObject == NULL
52035275 || PySSLSyscallErrorObject == NULL
52045276 || PySSLEOFErrorObject == NULL )
52055277 return NULL ;
52065278 if (PyDict_SetItemString (d , "SSLError" , PySSLErrorObject ) != 0
5279+ || PyDict_SetItemString (d , "SSLCertVerificationError" ,
5280+ PySSLCertVerificationErrorObject ) != 0
52075281 || PyDict_SetItemString (d , "SSLZeroReturnError" , PySSLZeroReturnErrorObject ) != 0
52085282 || PyDict_SetItemString (d , "SSLWantReadError" , PySSLWantReadErrorObject ) != 0
52095283 || PyDict_SetItemString (d , "SSLWantWriteError" , PySSLWantWriteErrorObject ) != 0
0 commit comments