bpo-28182: Expose OpenSSL verification results (#3412) · python/cpython@b3ad0e5 (original) (raw)
`@@ -66,6 +66,7 @@ static PySocketModule_APIObject PySocketModule;
`
66
66
``
67
67
`/* SSL error object */
`
68
68
`static PyObject *PySSLErrorObject;
`
``
69
`+
static PyObject *PySSLCertVerificationErrorObject;
`
69
70
`static PyObject *PySSLZeroReturnErrorObject;
`
70
71
`static PyObject *PySSLWantReadErrorObject;
`
71
72
`static PyObject *PySSLWantWriteErrorObject;
`
`@@ -386,6 +387,9 @@ typedef enum {
`
386
387
`PyDoc_STRVAR(SSLError_doc,
`
387
388
`"An error occurred in the SSL implementation.");
`
388
389
``
``
390
`+
PyDoc_STRVAR(SSLCertVerificationError_doc,
`
``
391
`+
"A certificate could not be verified.");
`
``
392
+
389
393
`PyDoc_STRVAR(SSLZeroReturnError_doc,
`
390
394
`"SSL/TLS session closed cleanly.");
`
391
395
``
`@@ -430,13 +434,16 @@ static PyType_Spec sslerror_type_spec = {
`
430
434
`};
`
431
435
``
432
436
`static 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)
`
435
439
`{
`
436
440
`PyObject *err_value = NULL, *reason_obj = NULL, *lib_obj = NULL;
`
``
441
`+
PyObject *verify_obj = NULL, *verify_code_obj = NULL;
`
437
442
`PyObject *init_value, *msg, *key;
`
438
443
`_Py_IDENTIFIER(reason);
`
439
444
`_Py_IDENTIFIER(library);
`
``
445
`+
_Py_IDENTIFIER(verify_message);
`
``
446
`+
_Py_IDENTIFIER(verify_code);
`
440
447
``
441
448
`if (errcode != 0) {
`
442
449
`int lib, reason;
`
`@@ -466,7 +473,50 @@ fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr,
`
466
473
`if (errstr == NULL)
`
467
474
`errstr = "unknown error";
`
468
475
``
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)
`
470
520
`msg = PyUnicode_FromFormat("[%S: %S] %s (_ssl.c:%d)",
`
471
521
`lib_obj, reason_obj, errstr, lineno);
`
472
522
`else if (lib_obj)
`
`@@ -490,17 +540,30 @@ fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr,
`
490
540
`reason_obj = Py_None;
`
491
541
`if (_PyObject_SetAttrId(err_value, &PyId_reason, reason_obj))
`
492
542
` goto fail;
`
``
543
+
493
544
`if (lib_obj == NULL)
`
494
545
`lib_obj = Py_None;
`
495
546
`if (_PyObject_SetAttrId(err_value, &PyId_library, lib_obj))
`
496
547
` 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
+
497
558
`PyErr_SetObject(type, err_value);
`
498
559
`fail:
`
499
560
`Py_XDECREF(err_value);
`
``
561
`+
Py_XDECREF(verify_code_obj);
`
``
562
`+
Py_XDECREF(verify_obj);
`
500
563
`}
`
501
564
``
502
565
`static 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)
`
504
567
`{
`
505
568
`PyObject *type = PySSLErrorObject;
`
506
569
`char *errstr = NULL;
`
`@@ -511,8 +574,8 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
`
511
574
`assert(ret <= 0);
`
512
575
`e = ERR_peek_last_error();
`
513
576
``
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);
`
516
579
``
517
580
`switch (err) {
`
518
581
`case SSL_ERROR_ZERO_RETURN:
`
`@@ -541,7 +604,7 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
`
541
604
`case SSL_ERROR_SYSCALL:
`
542
605
` {
`
543
606
`if (e == 0) {
`
544
``
`-
PySocketSockObject *s = GET_SOCKET(obj);
`
``
607
`+
PySocketSockObject *s = GET_SOCKET(sslsock);
`
545
608
`if (ret == 0 || (((PyObject *)s) == Py_None)) {
`
546
609
`p = PY_SSL_ERROR_EOF;
`
547
610
`type = PySSLEOFErrorObject;
`
`@@ -566,17 +629,22 @@ PySSL_SetError(PySSLSocket *obj, int ret, const char *filename, int lineno)
`
566
629
`case SSL_ERROR_SSL:
`
567
630
` {
`
568
631
`p = PY_SSL_ERROR_SSL;
`
569
``
`-
if (e == 0)
`
``
632
`+
if (e == 0) {
`
570
633
`/* possible? */
`
571
634
`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
`+
}
`
572
640
`break;
`
573
641
` }
`
574
642
`default:
`
575
643
`p = PY_SSL_ERROR_INVALID_ERROR_CODE;
`
576
644
`errstr = "Invalid error code";
`
577
645
` }
`
578
646
` }
`
579
``
`-
fill_and_set_sslerror(type, p, errstr, lineno, e);
`
``
647
`+
fill_and_set_sslerror(sslsock, type, p, errstr, lineno, e);
`
580
648
`ERR_clear_error();
`
581
649
`return NULL;
`
582
650
`}
`
`@@ -588,15 +656,11 @@ _setSSLError (const char *errstr, int errcode, const char *filename, int lineno)
`
588
656
`errcode = ERR_peek_last_error();
`
589
657
`else
`
590
658
`errcode = 0;
`
591
``
`-
fill_and_set_sslerror(PySSLErrorObject, errcode, errstr, lineno, errcode);
`
``
659
`+
fill_and_set_sslerror(NULL, PySSLErrorObject, errcode, errstr, lineno, errcode);
`
592
660
`ERR_clear_error();
`
593
661
`return NULL;
`
594
662
`}
`
595
663
``
596
``
`-
/*
`
597
``
`-
- SSL objects
`
598
``
`-
*/
`
599
``
-
600
664
`static PySSLSocket *
`
601
665
`newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
`
602
666
`enum py_ssl_server_or_client socket_type,
`
`@@ -656,7 +720,6 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
`
656
720
`if (server_hostname != NULL)
`
657
721
`SSL_set_tlsext_host_name(self->ssl, server_hostname);
`
658
722
`#endif
`
659
``
-
660
723
`/* If the socket is in non-blocking mode or timeout mode, set the BIO
`
661
724
` * to non-blocking mode (blocking is the default)
`
662
725
` */
`
`@@ -5130,7 +5193,7 @@ parse_openssl_version(unsigned long libver,
`
5130
5193
`PyMODINIT_FUNC
`
5131
5194
`PyInit__ssl(void)
`
5132
5195
`{
`
5133
``
`-
PyObject *m, *d, *r;
`
``
5196
`+
PyObject *m, *d, *r, *bases;
`
5134
5197
`unsigned long libver;
`
5135
5198
`unsigned int major, minor, fix, patch, status;
`
5136
5199
`PySocketModule_APIObject *socket_api;
`
`@@ -5182,6 +5245,14 @@ PyInit__ssl(void)
`
5182
5245
`if (PySSLErrorObject == NULL)
`
5183
5246
`return NULL;
`
5184
5247
``
``
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);
`
5185
5256
`PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc(
`
5186
5257
`"ssl.SSLZeroReturnError", SSLZeroReturnError_doc,
`
5187
5258
`PySSLErrorObject, NULL);
`
`@@ -5197,13 +5268,16 @@ PyInit__ssl(void)
`
5197
5268
`PySSLEOFErrorObject = PyErr_NewExceptionWithDoc(
`
5198
5269
`"ssl.SSLEOFError", SSLEOFError_doc,
`
5199
5270
`PySSLErrorObject, NULL);
`
5200
``
`-
if (PySSLZeroReturnErrorObject == NULL
`
``
5271
`+
if (PySSLCertVerificationErrorObject == NULL
`
``
5272
`+
|| PySSLZeroReturnErrorObject == NULL
`
5201
5273
`|| PySSLWantReadErrorObject == NULL
`
5202
5274
`|| PySSLWantWriteErrorObject == NULL
`
5203
5275
`|| PySSLSyscallErrorObject == NULL
`
5204
5276
`|| PySSLEOFErrorObject == NULL)
`
5205
5277
`return NULL;
`
5206
5278
`if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0
`
``
5279
`+
|| PyDict_SetItemString(d, "SSLCertVerificationError",
`
``
5280
`+
PySSLCertVerificationErrorObject) != 0
`
5207
5281
`|| PyDict_SetItemString(d, "SSLZeroReturnError", PySSLZeroReturnErrorObject) != 0
`
5208
5282
`|| PyDict_SetItemString(d, "SSLWantReadError", PySSLWantReadErrorObject) != 0
`
5209
5283
`|| PyDict_SetItemString(d, "SSLWantWriteError", PySSLWantWriteErrorObject) != 0
`