From 666991fc598bc312d72aff0078ecb553f0a968f1 Mon Sep 17 00:00:00 2001 From: Christian Heimes Date: Mon, 26 Apr 2021 15:01:40 +0200 Subject: [PATCH] bpo-18233: Add internal methods to access peer chain (GH-25467) The internal `_ssl._SSLSocket` object now provides methods to retrieve the peer cert chain and verified cert chain as a list of Certificate objects. Certificate objects have methods to convert the cert to a dict, PEM, or DER (ASN.1). These are private APIs for now. There is a slim chance to stabilize the approach and provide a public API for 3.10. Otherwise I'll provide a stable API in 3.11. Signed-off-by: Christian Heimes --- Lib/test/test_ssl.py | 69 ++++- .../2020-11-19-09-52-24.bpo-18369.qzvYH2.rst | 2 + Modules/_ssl.c | 84 +++++- Modules/_ssl.h | 31 ++- Modules/_ssl/cert.c | 245 ++++++++++++++++++ Modules/_ssl/clinic/cert.c.h | 60 +++++ Modules/_ssl/misc.c | 34 +++ Modules/clinic/_ssl.c.h | 36 ++- setup.py | 8 +- 9 files changed, 563 insertions(+), 6 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2020-11-19-09-52-24.bpo-18369.qzvYH2.rst create mode 100644 Modules/_ssl/cert.c create mode 100644 Modules/_ssl/clinic/cert.c.h create mode 100644 Modules/_ssl/misc.c diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 15d7bfaeec9..f2b26c4b140 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -32,6 +32,7 @@ ctypes = None ssl = import_helper.import_module("ssl") +import _ssl from ssl import TLSVersion, _TLSContentType, _TLSMessageType, _TLSAlertType @@ -297,7 +298,7 @@ def test_wrap_socket(sock, *, return context.wrap_socket(sock, **kwargs) -def testing_context(server_cert=SIGNED_CERTFILE): +def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True): """Create context client_context, server_context, hostname = testing_context() @@ -316,7 +317,8 @@ def testing_context(server_cert=SIGNED_CERTFILE): server_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) server_context.load_cert_chain(server_cert) - server_context.load_verify_locations(SIGNING_CA) + if server_chain: + server_context.load_verify_locations(SIGNING_CA) return client_context, server_context, hostname @@ -2482,6 +2484,12 @@ def run(self): elif stripped == b'GETCERT': cert = self.sslconn.getpeercert() self.write(repr(cert).encode("us-ascii") + b"\n") + elif stripped == b'VERIFIEDCHAIN': + certs = self.sslconn._sslobj.get_verified_chain() + self.write(len(certs).to_bytes(1, "big") + b"\n") + elif stripped == b'UNVERIFIEDCHAIN': + certs = self.sslconn._sslobj.get_unverified_chain() + self.write(len(certs).to_bytes(1, "big") + b"\n") else: if (support.verbose and self.server.connectionchatty): @@ -4567,6 +4575,63 @@ def test_bpo37428_pha_cert_none(self): # server cert has not been validated self.assertEqual(s.getpeercert(), {}) + def test_internal_chain_client(self): + client_context, server_context, hostname = testing_context( + server_chain=False + ) + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket( + socket.socket(), + server_hostname=hostname + ) as s: + s.connect((HOST, server.port)) + vc = s._sslobj.get_verified_chain() + self.assertEqual(len(vc), 2) + ee, ca = vc + uvc = s._sslobj.get_unverified_chain() + self.assertEqual(len(uvc), 1) + + self.assertEqual(ee, uvc[0]) + self.assertEqual(hash(ee), hash(uvc[0])) + self.assertEqual(repr(ee), repr(uvc[0])) + + self.assertNotEqual(ee, ca) + self.assertNotEqual(hash(ee), hash(ca)) + self.assertNotEqual(repr(ee), repr(ca)) + self.assertNotEqual(ee.get_info(), ca.get_info()) + self.assertIn("CN=localhost", repr(ee)) + self.assertIn("CN=our-ca-server", repr(ca)) + + pem = ee.public_bytes(_ssl.ENCODING_PEM) + der = ee.public_bytes(_ssl.ENCODING_DER) + self.assertIsInstance(pem, str) + self.assertIn("-----BEGIN CERTIFICATE-----", pem) + self.assertIsInstance(der, bytes) + self.assertEqual( + ssl.PEM_cert_to_DER_cert(pem), der + ) + + def test_internal_chain_server(self): + client_context, server_context, hostname = testing_context() + client_context.load_cert_chain(SIGNED_CERTFILE) + server_context.verify_mode = ssl.CERT_REQUIRED + server_context.maximum_version = ssl.TLSVersion.TLSv1_2 + + server = ThreadedEchoServer(context=server_context, chatty=False) + with server: + with client_context.wrap_socket( + socket.socket(), + server_hostname=hostname + ) as s: + s.connect((HOST, server.port)) + s.write(b'VERIFIEDCHAIN\n') + res = s.recv(1024) + self.assertEqual(res, b'\x02\n') + s.write(b'UNVERIFIEDCHAIN\n') + res = s.recv(1024) + self.assertEqual(res, b'\x02\n') + HAS_KEYLOG = hasattr(ssl.SSLContext, 'keylog_filename') requires_keylog = unittest.skipUnless( diff --git a/Misc/NEWS.d/next/Library/2020-11-19-09-52-24.bpo-18369.qzvYH2.rst b/Misc/NEWS.d/next/Library/2020-11-19-09-52-24.bpo-18369.qzvYH2.rst new file mode 100644 index 00000000000..1b97afbd2c4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-11-19-09-52-24.bpo-18369.qzvYH2.rst @@ -0,0 +1,2 @@ +Certificate and PrivateKey classes were added to the ssl module. +Certificates and keys can now be loaded from memory buffer, too. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index abc5c2cfa1a..65370c58a38 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -1706,6 +1706,9 @@ _certificate_to_der(_sslmodulestate *state, X509 *certificate) return retval; } +#include "_ssl/misc.c" +#include "_ssl/cert.c" + /*[clinic input] _ssl._test_decode_cert path: object(converter="PyUnicode_FSConverter") @@ -1798,6 +1801,70 @@ _ssl__SSLSocket_getpeercert_impl(PySSLSocket *self, int binary_mode) return result; } +/*[clinic input] +_ssl._SSLSocket.get_verified_chain + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_get_verified_chain_impl(PySSLSocket *self) +/*[clinic end generated code: output=802421163cdc3110 input=5fb0714f77e2bd51]*/ +{ + /* borrowed reference */ + STACK_OF(X509) *chain = SSL_get0_verified_chain(self->ssl); + if (chain == NULL) { + Py_RETURN_NONE; + } + return _PySSL_CertificateFromX509Stack(self->ctx->state, chain, 1); +} + +/*[clinic input] +_ssl._SSLSocket.get_unverified_chain + +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_get_unverified_chain_impl(PySSLSocket *self) +/*[clinic end generated code: output=5acdae414e13f913 input=78c33c360c635cb5]*/ +{ + PyObject *retval; + /* borrowed reference */ + /* TODO: include SSL_get_peer_certificate() for server-side sockets */ + STACK_OF(X509) *chain = SSL_get_peer_cert_chain(self->ssl); + if (chain == NULL) { + Py_RETURN_NONE; + } + retval = _PySSL_CertificateFromX509Stack(self->ctx->state, chain, 1); + if (retval == NULL) { + return NULL; + } + /* OpenSSL does not include peer cert for server side connections */ + if (self->socket_type == PY_SSL_SERVER) { + PyObject *peerobj = NULL; + X509 *peer = SSL_get_peer_certificate(self->ssl); + + if (peer == NULL) { + peerobj = Py_None; + Py_INCREF(peerobj); + } else { + /* consume X509 reference on success */ + peerobj = _PySSL_CertificateFromX509(self->ctx->state, peer, 0); + if (peerobj == NULL) { + X509_free(peer); + Py_DECREF(retval); + return NULL; + } + } + int res = PyList_Insert(retval, 0, peerobj); + Py_DECREF(peerobj); + if (res < 0) { + Py_DECREF(retval); + return NULL; + } + } + return retval; +} + static PyObject * cipher_to_tuple(const SSL_CIPHER *cipher) { @@ -2809,6 +2876,8 @@ static PyMethodDef PySSLMethods[] = { _SSL__SSLSOCKET_COMPRESSION_METHODDEF _SSL__SSLSOCKET_SHUTDOWN_METHODDEF _SSL__SSLSOCKET_VERIFY_CLIENT_POST_HANDSHAKE_METHODDEF + _SSL__SSLSOCKET_GET_UNVERIFIED_CHAIN_METHODDEF + _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF {NULL, NULL} }; @@ -5784,6 +5853,10 @@ sslmodule_init_constants(PyObject *m) X509_CHECK_FLAG_SINGLE_LABEL_SUBDOMAINS); #endif + /* file types */ + PyModule_AddIntConstant(m, "ENCODING_PEM", PY_SSL_ENCODING_PEM); + PyModule_AddIntConstant(m, "ENCODING_DER", PY_SSL_ENCODING_DER); + /* protocol versions */ PyModule_AddIntConstant(m, "PROTO_MINIMUM_SUPPORTED", PY_PROTO_MINIMUM_SUPPORTED); @@ -5986,6 +6059,12 @@ sslmodule_init_types(PyObject *module) if (state->PySSLSession_Type == NULL) return -1; + state->PySSLCertificate_Type = (PyTypeObject *)PyType_FromModuleAndSpec( + module, &PySSLCertificate_spec, NULL + ); + if (state->PySSLCertificate_Type == NULL) + return -1; + if (PyModule_AddType(module, state->PySSLContext_Type)) return -1; if (PyModule_AddType(module, state->PySSLSocket_Type)) @@ -5994,7 +6073,8 @@ sslmodule_init_types(PyObject *module) return -1; if (PyModule_AddType(module, state->PySSLSession_Type)) return -1; - + if (PyModule_AddType(module, state->PySSLCertificate_Type)) + return -1; return 0; } @@ -6017,6 +6097,7 @@ sslmodule_traverse(PyObject *m, visitproc visit, void *arg) Py_VISIT(state->PySSLSocket_Type); Py_VISIT(state->PySSLMemoryBIO_Type); Py_VISIT(state->PySSLSession_Type); + Py_VISIT(state->PySSLCertificate_Type); Py_VISIT(state->PySSLErrorObject); Py_VISIT(state->PySSLCertVerificationErrorObject); Py_VISIT(state->PySSLZeroReturnErrorObject); @@ -6041,6 +6122,7 @@ sslmodule_clear(PyObject *m) Py_CLEAR(state->PySSLSocket_Type); Py_CLEAR(state->PySSLMemoryBIO_Type); Py_CLEAR(state->PySSLSession_Type); + Py_CLEAR(state->PySSLCertificate_Type); Py_CLEAR(state->PySSLErrorObject); Py_CLEAR(state->PySSLCertVerificationErrorObject); Py_CLEAR(state->PySSLZeroReturnErrorObject); diff --git a/Modules/_ssl.h b/Modules/_ssl.h index 3fd517b6d36..5fe6504a9dd 100644 --- a/Modules/_ssl.h +++ b/Modules/_ssl.h @@ -1,6 +1,10 @@ #ifndef Py_SSL_H #define Py_SSL_H +/* OpenSSL header files */ +#include "openssl/evp.h" +#include "openssl/x509.h" + /* * ssl module state */ @@ -10,6 +14,7 @@ typedef struct { PyTypeObject *PySSLSocket_Type; PyTypeObject *PySSLMemoryBIO_Type; PyTypeObject *PySSLSession_Type; + PyTypeObject *PySSLCertificate_Type; /* SSL error object */ PyObject *PySSLErrorObject; PyObject *PySSLCertVerificationErrorObject; @@ -40,6 +45,30 @@ get_ssl_state(PyObject *module) (get_ssl_state(_PyType_GetModuleByDef(type, &_sslmodule_def))) #define get_state_ctx(c) (((PySSLContext *)(c))->state) #define get_state_sock(s) (((PySSLSocket *)(s))->ctx->state) -#define get_state_mbio(b) ((_sslmodulestate *)PyType_GetModuleState(Py_TYPE(b))) +#define get_state_obj(o) ((_sslmodulestate *)PyType_GetModuleState(Py_TYPE(o))) +#define get_state_mbio(b) get_state_obj(b) +#define get_state_cert(c) get_state_obj(c) + +/* ************************************************************************ + * certificate + */ + +enum py_ssl_encoding { + PY_SSL_ENCODING_PEM=X509_FILETYPE_PEM, + PY_SSL_ENCODING_DER=X509_FILETYPE_ASN1, + PY_SSL_ENCODING_PEM_AUX=X509_FILETYPE_PEM + 0x100, +}; + +typedef struct { + PyObject_HEAD + X509 *cert; + Py_hash_t hash; +} PySSLCertificate; + +/* ************************************************************************ + * helpers and utils + */ +static PyObject *_PySSL_BytesFromBIO(_sslmodulestate *state, BIO *bio); +static PyObject *_PySSL_UnicodeFromBIO(_sslmodulestate *state, BIO *bio, const char *error); #endif /* Py_SSL_H */ diff --git a/Modules/_ssl/cert.c b/Modules/_ssl/cert.c new file mode 100644 index 00000000000..d2c26800881 --- /dev/null +++ b/Modules/_ssl/cert.c @@ -0,0 +1,245 @@ +#include "Python.h" +#include "../_ssl.h" + +#include "openssl/err.h" +#include "openssl/bio.h" +#include "openssl/pem.h" +#include "openssl/x509.h" + +/*[clinic input] +module _ssl +class _ssl.Certificate "PySSLCertificate *" "PySSLCertificate_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=780fc647948cfffc]*/ + +#include "clinic/cert.c.h" + +static PyObject * +newCertificate(PyTypeObject *type, X509 *cert, int upref) +{ + PySSLCertificate *self; + + assert(type != NULL && type->tp_alloc != NULL); + assert(cert != NULL); + + self = (PySSLCertificate *) type->tp_alloc(type, 0); + if (self == NULL) { + return NULL; + } + if (upref == 1) { + X509_up_ref(cert); + } + self->cert = cert; + self->hash = -1; + + return (PyObject *) self; +} + +static PyObject * +_PySSL_CertificateFromX509(_sslmodulestate *state, X509 *cert, int upref) +{ + return newCertificate(state->PySSLCertificate_Type, cert, upref); +} + +static PyObject* +_PySSL_CertificateFromX509Stack(_sslmodulestate *state, STACK_OF(X509) *stack, int upref) +{ + int len, i; + PyObject *result = NULL; + + len = sk_X509_num(stack); + result = PyList_New(len); + if (result == NULL) { + return NULL; + } + for (i = 0; i < len; i++) { + X509 *cert = sk_X509_value(stack, i); + PyObject *ocert = _PySSL_CertificateFromX509(state, cert, upref); + if (ocert == NULL) { + Py_DECREF(result); + return NULL; + } + PyList_SetItem(result, i, ocert); + } + return result; +} + +/*[clinic input] +_ssl.Certificate.public_bytes + format: int(c_default="PY_SSL_ENCODING_PEM") = Encoding.PEM + +[clinic start generated code]*/ + +static PyObject * +_ssl_Certificate_public_bytes_impl(PySSLCertificate *self, int format) +/*[clinic end generated code: output=c01ddbb697429e12 input=4d38c45e874b0e64]*/ +{ + BIO *bio; + int retcode; + PyObject *result; + _sslmodulestate *state = get_state_cert(self); + + bio = BIO_new(BIO_s_mem()); + if (bio == NULL) { + PyErr_SetString(state->PySSLErrorObject, + "failed to allocate BIO"); + return NULL; + } + switch(format) { + case PY_SSL_ENCODING_PEM: + retcode = PEM_write_bio_X509(bio, self->cert); + break; + case PY_SSL_ENCODING_PEM_AUX: + retcode = PEM_write_bio_X509_AUX(bio, self->cert); + break; + case PY_SSL_ENCODING_DER: + retcode = i2d_X509_bio(bio, self->cert); + break; + default: + PyErr_SetString(PyExc_ValueError, "Unsupported format"); + BIO_free(bio); + return NULL; + } + if (retcode != 1) { + BIO_free(bio); + _setSSLError(state, NULL, 0, __FILE__, __LINE__); + return NULL; + } + if (format == PY_SSL_ENCODING_DER) { + result = _PySSL_BytesFromBIO(state, bio); + } else { + result = _PySSL_UnicodeFromBIO(state, bio, "error"); + } + BIO_free(bio); + return result; +} + + +/*[clinic input] +_ssl.Certificate.get_info + +[clinic start generated code]*/ + +static PyObject * +_ssl_Certificate_get_info_impl(PySSLCertificate *self) +/*[clinic end generated code: output=0f0deaac54f4408b input=ba2c1694b39d0778]*/ +{ + return _decode_certificate(get_state_cert(self), self->cert); +} + +static PyObject* +_x509name_print(_sslmodulestate *state, X509_NAME *name, int indent, unsigned long flags) +{ + PyObject *res; + BIO *biobuf; + + biobuf = BIO_new(BIO_s_mem()); + if (biobuf == NULL) { + PyErr_SetString(PyExc_MemoryError, "failed to allocate BIO"); + return NULL; + } + + if (X509_NAME_print_ex(biobuf, name, indent, flags) <= 0) { + _setSSLError(state, NULL, 0, __FILE__, __LINE__); + BIO_free(biobuf); + return NULL; + } + res = _PySSL_UnicodeFromBIO(state, biobuf, "strict"); + BIO_free(biobuf); + return res; +} + +/* ************************************************************************ + * PySSLCertificate_Type + */ + +static PyObject * +certificate_repr(PySSLCertificate *self) +{ + PyObject *osubject, *result; + + /* subject string is ASCII encoded, UTF-8 chars are quoted */ + osubject = _x509name_print( + get_state_cert(self), + X509_get_subject_name(self->cert), + 0, + XN_FLAG_RFC2253 + ); + if (osubject == NULL) + return NULL; + result = PyUnicode_FromFormat( + "<%s '%U'>", + Py_TYPE(self)->tp_name, osubject + ); + Py_DECREF(osubject); + return result; +} + +static Py_hash_t +certificate_hash(PySSLCertificate *self) +{ + if (self->hash == (Py_hash_t)-1) { + unsigned long hash; + hash = X509_subject_name_hash(self->cert); + if ((Py_hash_t)hash == (Py_hash_t)-1) { + self->hash = -2; + } else { + self->hash = (Py_hash_t)hash; + } + } + return self->hash; +} + +static PyObject * +certificate_richcompare(PySSLCertificate *self, PyObject *other, int op) +{ + int cmp; + _sslmodulestate *state = get_state_cert(self); + + if (Py_TYPE(other) != state->PySSLCertificate_Type) { + Py_RETURN_NOTIMPLEMENTED; + } + /* only support == and != */ + if ((op != Py_EQ) && (op != Py_NE)) { + Py_RETURN_NOTIMPLEMENTED; + } + cmp = X509_cmp(self->cert, ((PySSLCertificate*)other)->cert); + if (((op == Py_EQ) && (cmp == 0)) || ((op == Py_NE) && (cmp != 0))) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } +} + +static void +certificate_dealloc(PySSLCertificate *self) +{ + PyTypeObject *tp = Py_TYPE(self); + X509_free(self->cert); + Py_TYPE(self)->tp_free(self); + Py_DECREF(tp); +} + +static PyMethodDef certificate_methods[] = { + /* methods */ + _SSL_CERTIFICATE_PUBLIC_BYTES_METHODDEF + _SSL_CERTIFICATE_GET_INFO_METHODDEF + {NULL, NULL} +}; + +static PyType_Slot PySSLCertificate_slots[] = { + {Py_tp_dealloc, certificate_dealloc}, + {Py_tp_repr, certificate_repr}, + {Py_tp_hash, certificate_hash}, + {Py_tp_richcompare, certificate_richcompare}, + {Py_tp_methods, certificate_methods}, + {0, 0}, +}; + +static PyType_Spec PySSLCertificate_spec = { + "_ssl.Certificate", + sizeof(PySSLCertificate), + 0, + Py_TPFLAGS_DEFAULT, + PySSLCertificate_slots, +}; diff --git a/Modules/_ssl/clinic/cert.c.h b/Modules/_ssl/clinic/cert.c.h new file mode 100644 index 00000000000..c4377128b36 --- /dev/null +++ b/Modules/_ssl/clinic/cert.c.h @@ -0,0 +1,60 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +PyDoc_STRVAR(_ssl_Certificate_public_bytes__doc__, +"public_bytes($self, /, format=Encoding.PEM)\n" +"--\n" +"\n"); + +#define _SSL_CERTIFICATE_PUBLIC_BYTES_METHODDEF \ + {"public_bytes", (PyCFunction)(void(*)(void))_ssl_Certificate_public_bytes, METH_FASTCALL|METH_KEYWORDS, _ssl_Certificate_public_bytes__doc__}, + +static PyObject * +_ssl_Certificate_public_bytes_impl(PySSLCertificate *self, int format); + +static PyObject * +_ssl_Certificate_public_bytes(PySSLCertificate *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"format", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "public_bytes", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int format = PY_SSL_ENCODING_PEM; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + format = _PyLong_AsInt(args[0]); + if (format == -1 && PyErr_Occurred()) { + goto exit; + } +skip_optional_pos: + return_value = _ssl_Certificate_public_bytes_impl(self, format); + +exit: + return return_value; +} + +PyDoc_STRVAR(_ssl_Certificate_get_info__doc__, +"get_info($self, /)\n" +"--\n" +"\n"); + +#define _SSL_CERTIFICATE_GET_INFO_METHODDEF \ + {"get_info", (PyCFunction)_ssl_Certificate_get_info, METH_NOARGS, _ssl_Certificate_get_info__doc__}, + +static PyObject * +_ssl_Certificate_get_info_impl(PySSLCertificate *self); + +static PyObject * +_ssl_Certificate_get_info(PySSLCertificate *self, PyObject *Py_UNUSED(ignored)) +{ + return _ssl_Certificate_get_info_impl(self); +} +/*[clinic end generated code: output=569d161749ead2da input=a9049054013a1b77]*/ diff --git a/Modules/_ssl/misc.c b/Modules/_ssl/misc.c new file mode 100644 index 00000000000..4de091d57ef --- /dev/null +++ b/Modules/_ssl/misc.c @@ -0,0 +1,34 @@ +#include "Python.h" +#include "../_ssl.h" + +#include "openssl/bio.h" + +/* BIO_s_mem() to PyBytes + */ +static PyObject * +_PySSL_BytesFromBIO(_sslmodulestate *state, BIO *bio) +{ + long size; + char *data = NULL; + size = BIO_get_mem_data(bio, &data); + if (data == NULL || size < 0) { + PyErr_SetString(PyExc_ValueError, "Not a memory BIO"); + return NULL; + } + return PyBytes_FromStringAndSize(data, size); +} + +/* BIO_s_mem() to PyUnicode + */ +static PyObject * +_PySSL_UnicodeFromBIO(_sslmodulestate *state, BIO *bio, const char *error) +{ + long size; + char *data = NULL; + size = BIO_get_mem_data(bio, &data); + if (data == NULL || size < 0) { + PyErr_SetString(PyExc_ValueError, "Not a memory BIO"); + return NULL; + } + return PyUnicode_DecodeUTF8(data, size, error); +} diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index c209c63c022..b153c30cf39 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -88,6 +88,40 @@ exit: return return_value; } +PyDoc_STRVAR(_ssl__SSLSocket_get_verified_chain__doc__, +"get_verified_chain($self, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF \ + {"get_verified_chain", (PyCFunction)_ssl__SSLSocket_get_verified_chain, METH_NOARGS, _ssl__SSLSocket_get_verified_chain__doc__}, + +static PyObject * +_ssl__SSLSocket_get_verified_chain_impl(PySSLSocket *self); + +static PyObject * +_ssl__SSLSocket_get_verified_chain(PySSLSocket *self, PyObject *Py_UNUSED(ignored)) +{ + return _ssl__SSLSocket_get_verified_chain_impl(self); +} + +PyDoc_STRVAR(_ssl__SSLSocket_get_unverified_chain__doc__, +"get_unverified_chain($self, /)\n" +"--\n" +"\n"); + +#define _SSL__SSLSOCKET_GET_UNVERIFIED_CHAIN_METHODDEF \ + {"get_unverified_chain", (PyCFunction)_ssl__SSLSocket_get_unverified_chain, METH_NOARGS, _ssl__SSLSocket_get_unverified_chain__doc__}, + +static PyObject * +_ssl__SSLSocket_get_unverified_chain_impl(PySSLSocket *self); + +static PyObject * +_ssl__SSLSocket_get_unverified_chain(PySSLSocket *self, PyObject *Py_UNUSED(ignored)) +{ + return _ssl__SSLSocket_get_unverified_chain_impl(self); +} + PyDoc_STRVAR(_ssl__SSLSocket_shared_ciphers__doc__, "shared_ciphers($self, /)\n" "--\n" @@ -1324,4 +1358,4 @@ exit: #ifndef _SSL_ENUM_CRLS_METHODDEF #define _SSL_ENUM_CRLS_METHODDEF #endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */ -/*[clinic end generated code: output=8736d838c9059151 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3b6f4471fb187d85 input=a9049054013a1b77]*/ diff --git a/setup.py b/setup.py index 4f4b42d1d9d..27e5f392cfd 100644 --- a/setup.py +++ b/setup.py @@ -2472,7 +2472,13 @@ def split_var(name, sep): Extension( '_ssl', ['_ssl.c'], - depends=['socketmodule.h', '_ssl/debughelpers.c', '_ssl.h'], + depends=[ + 'socketmodule.h', + '_ssl.h', + '_ssl/debughelpers.c', + '_ssl/misc.c', + '_ssl/cert.c', + ], **openssl_extension_kwargs ) )