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

``

`-

`

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

`