[Python-checkins] bpo-18233: Add internal methods to access peer chain (GH-25467)

tiran webhook-mailer at python.org
Mon Apr 26 09:01:44 EDT 2021


https://github.com/python/cpython/commit/666991fc598bc312d72aff0078ecb553f0a968f1
commit: 666991fc598bc312d72aff0078ecb553f0a968f1
branch: master
author: Christian Heimes <christian at python.org>
committer: tiran <christian at python.org>
date: 2021-04-26T15:01:40+02:00
summary:

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 <christian at python.org>

files:
A Misc/NEWS.d/next/Library/2020-11-19-09-52-24.bpo-18369.qzvYH2.rst
A Modules/_ssl/cert.c
A Modules/_ssl/clinic/cert.c.h
A Modules/_ssl/misc.c
M Lib/test/test_ssl.py
M Modules/_ssl.c
M Modules/_ssl.h
M Modules/clinic/_ssl.c.h
M setup.py

diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py
index 15d7bfaeec9e0..f2b26c4b1405e 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 0000000000000..1b97afbd2c40f
--- /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 abc5c2cfa1a79..65370c58a38b5 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 3fd517b6d3683..5fe6504a9dd7b 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 0000000000000..d2c2680088115
--- /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 0000000000000..c4377128b36e2
--- /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 0000000000000..4de091d57efef
--- /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 c209c63c02256..b153c30cf39ae 100644
--- a/Modules/clinic/_ssl.c.h
+++ b/Modules/clinic/_ssl.c.h
@@ -88,6 +88,40 @@ _ssl__SSLSocket_getpeercert(PySSLSocket *self, PyObject *const *args, Py_ssize_t
     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 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje
 #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 4f4b42d1d9d6a..27e5f392cfd33 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
             )
         )



More information about the Python-checkins mailing list