From 33bfd92f6e85d7d1b465f51ab55bbe718061d1bc Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Fri, 10 Jan 2020 16:06:14 +0000 Subject: [PATCH 1/7] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst diff --git a/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst b/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst new file mode 100644 index 00000000000000..83501223c1edc3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst @@ -0,0 +1 @@ +Add :meth:`ssl.SSLSocket.getpeercertchain` for accessing the certificate chain of SSL connections. \ No newline at end of file From 7008f61a1b93b321905d1ecefa10f5fa12d46b28 Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Fri, 10 Jan 2020 17:22:21 +0100 Subject: [PATCH 2/7] bpo-18233: Add SSLSocket.getpeercertchain() Based on the patch provided by Christian Heimes (christian.heimes) and updated by Mariusz Masztalerczuk (mmasztalerczuk). --- Doc/library/ssl.rst | 15 +++++ Lib/ssl.py | 13 ++++ Lib/test/test_ssl.py | 30 ++++++++++ Modules/_ssl.c | 128 ++++++++++++++++++++++++++++++++++++++++ Modules/clinic/_ssl.c.h | 52 +++++++++++++++- 5 files changed, 237 insertions(+), 1 deletion(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 1cfd165202d0ef..1a1e0af76aba4a 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1259,6 +1259,21 @@ SSL sockets also have the following additional methods and attributes: .. versionchanged:: 3.9 IPv6 address strings no longer have a trailing new line. +.. method:: SSLSocket.getpeercertchain(binary_form=False, validate=True) + + Returns certificate chain for the peer. If no chain is provided, returns + None. Otherwise returns a tuple of dicts containing information about the + certificates. The chain starts with the leaf certificate and ends with the + root certificate. If called on the client side, the leaf certificate is the + peer's certificate. + + If the optional argument *binary_form* is True, return a list of *binary_form*-encoded copies + of the certificates. + If the optional argument *validate* is False, return the peer's cert chain + without any validation and without the root CA cert."); + + .. versionadded:: 3.9 + .. method:: SSLSocket.cipher() Returns a three-value tuple containing the name of the cipher being used, the diff --git a/Lib/ssl.py b/Lib/ssl.py index 30f4e5934febf9..d6de90139ac4cf 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -905,6 +905,13 @@ def getpeercert(self, binary_form=False): """ return self._sslobj.getpeercert(binary_form) + def getpeercertchain(self, binary_form=False, validate=True): + """"Returns the certificate chain of the SSL connection + as tuple of dicts. + + Return None if no chain is provieded.""" + return self._sslobj.getpeercertchain(binary_form, validate) + def selected_npn_protocol(self): """Return the currently selected NPN protocol as a string, or ``None`` if a next protocol was not negotiated or if NPN is not supported by one @@ -1123,6 +1130,12 @@ def getpeercert(self, binary_form=False): self._check_connected() return self._sslobj.getpeercert(binary_form) + @_sslcopydoc + def getpeercertchain(self, binary_form=False, validate=True): + self._checkClosed() + self._check_connected() + return self._sslobj.getpeercertchain(binary_form, validate) + @_sslcopydoc def selected_npn_protocol(self): self._checkClosed() diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 67850c34e00c20..3053b2103afef2 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2160,6 +2160,36 @@ def test_get_ca_certs_capath(self): self.assertTrue(cert) self.assertEqual(len(ctx.get_ca_certs()), 1) + def test_getpeercertchain(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=CAPATH) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + try: + peer_cert = s.getpeercert() + peer_cert_bin = s.getpeercert(True) + chain = s.getpeercertchain() + chain_bin = s.getpeercertchain(True) + chain_no_validate = s.getpeercertchain(validate=False) + chain_bin_no_validate = s.getpeercertchain(True, False) + finally: + self.assertTrue(peer_cert) + self.assertEqual(len(chain), 2) + self.assertTrue(peer_cert_bin) + self.assertEqual(len(chain_bin), 2) + + # ca cert + ca_certs = ctx.get_ca_certs() + self.assertEqual(len(ca_certs), 1) + test_get_ca_certsert = ca_certs[0] + ca_cert_bin = ctx.get_ca_certs(True)[0] + + self.assertEqual(chain, (peer_cert, test_get_ca_certsert)) + self.assertEqual(chain_bin, (peer_cert_bin, ca_cert_bin)) + self.assertEqual(chain_no_validate, (peer_cert,)) + self.assertEqual(chain_bin_no_validate, (peer_cert_bin,)) + @needs_sni def test_context_setget(self): # Check that the context of a connected socket can be replaced. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 96d2796fcfad48..3112025367bf2a 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2096,6 +2096,133 @@ _ssl__SSLSocket_cipher_impl(PySSLSocket *self) return cipher_to_tuple(current); } +/*[clinic input] +_ssl._SSLSocket.getpeercertchain + der as binary_mode: bool = False + validate: bool = True +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, + int validate) +/*[clinic end generated code: output=8094e6d78d27eb9a input=f4dcd181d0d163eb]*/ +{ + int len, i; + PyObject *retval = NULL, *ci=NULL; + STACK_OF(X509) *peer_chain; /* reference */ + + assert((self->ctx != NULL) && (self->ctx->ctx != NULL)); + if (self->ssl == NULL) { + Py_RETURN_NONE; + } + + /* The peer just transmits the intermediate cert chain EXCLUDING the root + * CA certificate as this side is suppose to have a copy of the root + * certificate for verification. */ + if (validate) { +#ifdef OPENSSL_VERSION_1_1 + peer_chain = SSL_get0_verified_chain(self->ssl); + long ret = SSL_get_verify_result(self->ssl); + if (ret != X509_V_OK) { +#ifdef SSL_R_CERTIFICATE_VERIFY_FAILED + long e = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED); +#else + long e = ERR_PACK(ERR_LIB_SSL, 0, 134); +#endif + fill_and_set_sslerror(self, PySSLCertVerificationErrorObject, PY_SSL_ERROR_SSL, NULL, __LINE__, e); + return NULL; + } +#else + X509 *peer_cert = SSL_get_peer_certificate(self->ssl); + if (peer_cert == NULL) + Py_RETURN_NONE; + + STACK_OF(X509) *chain = SSL_get_peer_cert_chain(self->ssl); + if (chain == NULL) { + X509_free(peer_cert); + Py_RETURN_NONE; + } + X509_STORE_CTX *store_ctx; + + /* Initialize a store context with store (for root CA certs), the + * peer's cert and the peer's chain with intermediate CA certs. */ + if ((store_ctx = X509_STORE_CTX_new()) == NULL) { + X509_free(peer_cert); + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } + + if (!X509_STORE_CTX_init(store_ctx, + SSL_CTX_get_cert_store(self->ctx->ctx), + peer_cert, chain)) { +#ifdef SSL_R_CERTIFICATE_VERIFY_FAILED + long e = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED); +#else + long e = ERR_PACK(ERR_LIB_SSL, 0, 134); +#endif + fill_and_set_sslerror(self, PySSLCertVerificationErrorObject, PY_SSL_ERROR_SSL, NULL, __LINE__, e); + X509_free(peer_cert); + X509_STORE_CTX_free(store_ctx); + goto end; + } + X509_free(peer_cert); + + /* Validate peer cert using its intermediate CA certs and the + * context's root CA certs. */ + if (X509_verify_cert(store_ctx) <= 0) { + // _setX509StoreContextError(self, store_ctx, __FILE__, __LINE__); +#ifdef SSL_R_CERTIFICATE_VERIFY_FAILED + long e = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED); +#else + long e = ERR_PACK(ERR_LIB_SSL, 0, 134); +#endif + fill_and_set_sslerror(self, PySSLCertVerificationErrorObject, PY_SSL_ERROR_SSL, NULL, __LINE__, e); + X509_STORE_CTX_free(store_ctx); + goto end; + } + + /* Get chain from store context */ + peer_chain = X509_STORE_CTX_get1_chain(store_ctx); + X509_STORE_CTX_free(store_ctx); +#endif + } else { + peer_chain = SSL_get_peer_cert_chain(self->ssl); + } + + if (peer_chain == NULL) { + Py_RETURN_NONE; + } + + len = sk_X509_num(peer_chain); + + if ((retval = PyTuple_New(len)) == NULL) { + return NULL; + } + + for (i = 0; i < len; i++){ + X509 *cert = sk_X509_value(peer_chain, i); + if (binary_mode) { + ci = _certificate_to_der(cert); + } else { + ci = _decode_certificate(cert); + } + + if (ci == NULL) { + Py_CLEAR(retval); + goto end; + } + PyTuple_SET_ITEM(retval, i, ci); + } + + end: +#ifndef OPENSSL_VERSION_1_1 + if (validate && (peer_chain != NULL)) { + sk_X509_pop_free(peer_chain, X509_free); + } +#endif + return retval; +} + /*[clinic input] _ssl._SSLSocket.version [clinic start generated code]*/ @@ -3000,6 +3127,7 @@ static PyMethodDef PySSLMethods[] = { _SSL__SSLSOCKET_GET_CHANNEL_BINDING_METHODDEF _SSL__SSLSOCKET_CIPHER_METHODDEF _SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF + _SSL__SSLSOCKET_GETPEERCERTCHAIN_METHODDEF _SSL__SSLSOCKET_VERSION_METHODDEF _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 43469d3c358242..41f6fcd4c3bc9a 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -122,6 +122,56 @@ _ssl__SSLSocket_cipher(PySSLSocket *self, PyObject *Py_UNUSED(ignored)) return _ssl__SSLSocket_cipher_impl(self); } +PyDoc_STRVAR(_ssl__SSLSocket_getpeercertchain__doc__, +"getpeercertchain($self, /, der=False, validate=True)\n" +"--\n" +"\n"); + +#define _SSL__SSLSOCKET_GETPEERCERTCHAIN_METHODDEF \ + {"getpeercertchain", (PyCFunction)(void(*)(void))_ssl__SSLSocket_getpeercertchain, METH_FASTCALL|METH_KEYWORDS, _ssl__SSLSocket_getpeercertchain__doc__}, + +static PyObject * +_ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, + int validate); + +static PyObject * +_ssl__SSLSocket_getpeercertchain(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"der", "validate", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "getpeercertchain", 0}; + PyObject *argsbuf[2]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int binary_mode = 0; + int validate = 1; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + if (args[0]) { + binary_mode = PyObject_IsTrue(args[0]); + if (binary_mode < 0) { + goto exit; + } + if (!--noptargs) { + goto skip_optional_pos; + } + } + validate = PyObject_IsTrue(args[1]); + if (validate < 0) { + goto exit; + } +skip_optional_pos: + return_value = _ssl__SSLSocket_getpeercertchain_impl(self, binary_mode, validate); + +exit: + return return_value; +} + PyDoc_STRVAR(_ssl__SSLSocket_version__doc__, "version($self, /)\n" "--\n" @@ -1447,4 +1497,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=2bb53a80040c9b35 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=7898c106854115f2 input=a9049054013a1b77]*/ From 4243cc6c1994009f35c7bb52728ba450f4ed8f9f Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Mon, 13 Jan 2020 14:13:36 +0100 Subject: [PATCH 3/7] bpo-18233: Always return peer certificate from SSLSocket.getpeercertchain() --- Modules/_ssl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 3112025367bf2a..def652353de67b 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2187,6 +2187,11 @@ _ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, #endif } else { peer_chain = SSL_get_peer_cert_chain(self->ssl); + if (self->socket_type == PY_SSL_SERVER) { + X509 *peer_cert = SSL_get_peer_certificate(self->ssl); + if (peer_cert != NULL) + sk_X509_insert(peer_chain, peer_cert, 0); + } } if (peer_chain == NULL) { From a7c641d994a9ad36ee2da77044b65e98c7938588 Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Mon, 13 Jan 2020 08:59:42 +0100 Subject: [PATCH 4/7] bpo-18233: Only support getpeercertchain(validate=True) with OpenSSL 1.1.0+ --- Lib/test/test_ssl.py | 22 +++++++++++----- Modules/_ssl.c | 61 ++++---------------------------------------- 2 files changed, 21 insertions(+), 62 deletions(-) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 3053b2103afef2..128d2a64378837 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2169,15 +2169,24 @@ def test_getpeercertchain(self): try: peer_cert = s.getpeercert() peer_cert_bin = s.getpeercert(True) - chain = s.getpeercertchain() - chain_bin = s.getpeercertchain(True) + if IS_OPENSSL_1_1_0: + chain = s.getpeercertchain() + chain_bin = s.getpeercertchain(True) + else: + self.assertRaisesRegex( + Exception, r'only supported by OpenSSL 1\.1\.0', + s.getpeercertchain) + self.assertRaisesRegex( + Exception, r'only supported by OpenSSL 1\.1\.0', + s.getpeercertchain, True) chain_no_validate = s.getpeercertchain(validate=False) chain_bin_no_validate = s.getpeercertchain(True, False) finally: self.assertTrue(peer_cert) - self.assertEqual(len(chain), 2) self.assertTrue(peer_cert_bin) - self.assertEqual(len(chain_bin), 2) + if IS_OPENSSL_1_1_0: + self.assertEqual(len(chain), 2) + self.assertEqual(len(chain_bin), 2) # ca cert ca_certs = ctx.get_ca_certs() @@ -2185,8 +2194,9 @@ def test_getpeercertchain(self): test_get_ca_certsert = ca_certs[0] ca_cert_bin = ctx.get_ca_certs(True)[0] - self.assertEqual(chain, (peer_cert, test_get_ca_certsert)) - self.assertEqual(chain_bin, (peer_cert_bin, ca_cert_bin)) + if IS_OPENSSL_1_1_0: + self.assertEqual(chain, (peer_cert, test_get_ca_certsert)) + self.assertEqual(chain_bin, (peer_cert_bin, ca_cert_bin)) self.assertEqual(chain_no_validate, (peer_cert,)) self.assertEqual(chain_bin_no_validate, (peer_cert_bin,)) diff --git a/Modules/_ssl.c b/Modules/_ssl.c index def652353de67b..d1a770a56b08fa 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2133,57 +2133,11 @@ _ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, return NULL; } #else - X509 *peer_cert = SSL_get_peer_certificate(self->ssl); - if (peer_cert == NULL) - Py_RETURN_NONE; - - STACK_OF(X509) *chain = SSL_get_peer_cert_chain(self->ssl); - if (chain == NULL) { - X509_free(peer_cert); - Py_RETURN_NONE; - } - X509_STORE_CTX *store_ctx; - - /* Initialize a store context with store (for root CA certs), the - * peer's cert and the peer's chain with intermediate CA certs. */ - if ((store_ctx = X509_STORE_CTX_new()) == NULL) { - X509_free(peer_cert); - _setSSLError(NULL, 0, __FILE__, __LINE__); - return NULL; - } - - if (!X509_STORE_CTX_init(store_ctx, - SSL_CTX_get_cert_store(self->ctx->ctx), - peer_cert, chain)) { -#ifdef SSL_R_CERTIFICATE_VERIFY_FAILED - long e = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED); -#else - long e = ERR_PACK(ERR_LIB_SSL, 0, 134); -#endif - fill_and_set_sslerror(self, PySSLCertVerificationErrorObject, PY_SSL_ERROR_SSL, NULL, __LINE__, e); - X509_free(peer_cert); - X509_STORE_CTX_free(store_ctx); - goto end; - } - X509_free(peer_cert); - - /* Validate peer cert using its intermediate CA certs and the - * context's root CA certs. */ - if (X509_verify_cert(store_ctx) <= 0) { - // _setX509StoreContextError(self, store_ctx, __FILE__, __LINE__); -#ifdef SSL_R_CERTIFICATE_VERIFY_FAILED - long e = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED); -#else - long e = ERR_PACK(ERR_LIB_SSL, 0, 134); -#endif - fill_and_set_sslerror(self, PySSLCertVerificationErrorObject, PY_SSL_ERROR_SSL, NULL, __LINE__, e); - X509_STORE_CTX_free(store_ctx); - goto end; - } - - /* Get chain from store context */ - peer_chain = X509_STORE_CTX_get1_chain(store_ctx); - X509_STORE_CTX_free(store_ctx); + PyErr_SetString( + PyExc_Exception, + "Getting verified certificate chains with SSL_get0_verified_chain" + " is only supported by OpenSSL 1.1.0 and later"); + return NULL; #endif } else { peer_chain = SSL_get_peer_cert_chain(self->ssl); @@ -2220,11 +2174,6 @@ _ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, } end: -#ifndef OPENSSL_VERSION_1_1 - if (validate && (peer_chain != NULL)) { - sk_X509_pop_free(peer_chain, X509_free); - } -#endif return retval; } From 3324381eb65e1dd392a0d838daa25d87bc5e5be5 Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Thu, 16 Jan 2020 12:20:01 +0100 Subject: [PATCH 5/7] bpo-18233: Review of SSLSocket.get_peer_cert_chain and SSLSocket.get_verified_chain --- Doc/library/ssl.rst | 41 +++++-- Lib/ssl.py | 29 +++-- Lib/test/test_ssl.py | 39 ++++-- .../2020-01-10-16-06-13.bpo-18233.EsqH1K.rst | 2 +- Modules/_ssl.c | 113 ++++++++++-------- Modules/clinic/_ssl.c.h | 84 +++++++++---- 6 files changed, 201 insertions(+), 107 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 1a1e0af76aba4a..976cbbe4d6ef09 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1259,20 +1259,39 @@ SSL sockets also have the following additional methods and attributes: .. versionchanged:: 3.9 IPv6 address strings no longer have a trailing new line. -.. method:: SSLSocket.getpeercertchain(binary_form=False, validate=True) +.. method:: SSLSocket.get_peer_cert_chain(binary_form=False) - Returns certificate chain for the peer. If no chain is provided, returns - None. Otherwise returns a tuple of dicts containing information about the - certificates. The chain starts with the leaf certificate and ends with the - root certificate. If called on the client side, the leaf certificate is the - peer's certificate. + Returns an **unverified** certificate chain for the peer. If no chain is + provided, returns :const:`None`. Otherwise returns a tuple of dicts + containing information about the certificates. The chain starts with the + leaf certificate and ends with the root certificate. Return :const:`None` + if the session is resumed as peers do not send certificates. - If the optional argument *binary_form* is True, return a list of *binary_form*-encoded copies - of the certificates. - If the optional argument *validate* is False, return the peer's cert chain - without any validation and without the root CA cert."); + If the ``binary_form`` parameter is :const:`True`, and a chain is available, + this method returns a tuple with each element corresponding to the + DER-encoded form of the entire certificate as a sequence of bytes. - .. versionadded:: 3.9 + .. versionadded:: 3.9 + + .. warning:: + This is not a verified chain. See :meth:`ssl.SSLSocket.get_verified_chain`. + +.. method:: SSLSocket.get_verified_chain(binary_form=False) + + Returns a verified certificate chain for the peer. If no chain is provided, + returns :const:`None`. Otherwise returns a tuple of dicts containing + information about the certificates. The chain starts with the leaf + certificate and ends with the root certificate. Return :const:`None` if the + session is resumed as peers do not send certificates. + + If the ``binary_form`` parameter is :const:`True`, and a chain is available, + this method returns a tuple with each element corresponding to the + DER-encoded form of the entire certificate as a sequence of bytes. + + .. versionadded:: 3.9 + + .. note:: + This features requires OpenSSL 1.1.0 or newer. .. method:: SSLSocket.cipher() diff --git a/Lib/ssl.py b/Lib/ssl.py index d6de90139ac4cf..1c0d2c510d3500 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -905,12 +905,20 @@ def getpeercert(self, binary_form=False): """ return self._sslobj.getpeercert(binary_form) - def getpeercertchain(self, binary_form=False, validate=True): - """"Returns the certificate chain of the SSL connection - as tuple of dicts. + def get_peer_cert_chain(self, binary_form=False): + """"Returns the certificate chain of the SSL connection as a tuple of + dicts. It is *not* a verified chain. - Return None if no chain is provieded.""" - return self._sslobj.getpeercertchain(binary_form, validate) + Return ``None`` if no chain is provided.""" + return self._sslobj.get_peer_cert_chain(binary_form) + + if hasattr(_ssl._SSLSocket, 'get_verified_chain'): + def get_verified_chain(self, binary_form=False): + """"Returns the verified certificate chain of the SSL connection as a + tuple of dicts. + + Return ``None`` if no chain is provided.""" + return self._sslobj.get_verified_chain(binary_form) def selected_npn_protocol(self): """Return the currently selected NPN protocol as a string, or ``None`` @@ -1131,10 +1139,17 @@ def getpeercert(self, binary_form=False): return self._sslobj.getpeercert(binary_form) @_sslcopydoc - def getpeercertchain(self, binary_form=False, validate=True): + def get_peer_cert_chain(self, binary_form=False): self._checkClosed() self._check_connected() - return self._sslobj.getpeercertchain(binary_form, validate) + return self._sslobj.get_peer_cert_chain(binary_form) + + if hasattr(_ssl._SSLSocket, 'get_verified_chain'): + @_sslcopydoc + def get_verified_chain(self, binary_form=False): + self._checkClosed() + self._check_connected() + return self._sslobj.get_verified_chain(binary_form) @_sslcopydoc def selected_npn_protocol(self): diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 128d2a64378837..c8b2dea29b467e 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2160,7 +2160,29 @@ def test_get_ca_certs_capath(self): self.assertTrue(cert) self.assertEqual(len(ctx.get_ca_certs()), 1) - def test_getpeercertchain(self): + def test_get_peer_cert_chain(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=CAPATH) + with ctx.wrap_socket(socket.socket(socket.AF_INET)) as s: + s.connect(self.server_addr) + try: + peer_cert = s.getpeercert() + peer_cert_bin = s.getpeercert(True) + chain_no_validate = s.get_peer_cert_chain() + chain_bin_no_validate = s.get_peer_cert_chain(True) + finally: + self.assertTrue(peer_cert) + self.assertTrue(peer_cert_bin) + + # ca cert + ca_certs = ctx.get_ca_certs() + self.assertEqual(len(ca_certs), 1) + + self.assertEqual(chain_no_validate, (peer_cert,)) + self.assertEqual(chain_bin_no_validate, (peer_cert_bin,)) + + def test_get_verified_chain(self): ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) ctx.verify_mode = ssl.CERT_REQUIRED ctx.load_verify_locations(capath=CAPATH) @@ -2170,17 +2192,10 @@ def test_getpeercertchain(self): peer_cert = s.getpeercert() peer_cert_bin = s.getpeercert(True) if IS_OPENSSL_1_1_0: - chain = s.getpeercertchain() - chain_bin = s.getpeercertchain(True) + chain = s.get_verified_chain() + chain_bin = s.get_verified_chain(True) else: - self.assertRaisesRegex( - Exception, r'only supported by OpenSSL 1\.1\.0', - s.getpeercertchain) - self.assertRaisesRegex( - Exception, r'only supported by OpenSSL 1\.1\.0', - s.getpeercertchain, True) - chain_no_validate = s.getpeercertchain(validate=False) - chain_bin_no_validate = s.getpeercertchain(True, False) + self.assertFalse(hasattr(s, 'get_verified_chain')) finally: self.assertTrue(peer_cert) self.assertTrue(peer_cert_bin) @@ -2197,8 +2212,6 @@ def test_getpeercertchain(self): if IS_OPENSSL_1_1_0: self.assertEqual(chain, (peer_cert, test_get_ca_certsert)) self.assertEqual(chain_bin, (peer_cert_bin, ca_cert_bin)) - self.assertEqual(chain_no_validate, (peer_cert,)) - self.assertEqual(chain_bin_no_validate, (peer_cert_bin,)) @needs_sni def test_context_setget(self): diff --git a/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst b/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst index 83501223c1edc3..956deb0b9686de 100644 --- a/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst +++ b/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst @@ -1 +1 @@ -Add :meth:`ssl.SSLSocket.getpeercertchain` for accessing the certificate chain of SSL connections. \ No newline at end of file +Add :meth:`ssl.SSLSocket.get_peer_cert_chain` and :meth:`ssl.SSLSocket.get_verified_chain` for accessing the certificate chain of SSL connections. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index d1a770a56b08fa..b7966bca14c2fa 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2096,64 +2096,17 @@ _ssl__SSLSocket_cipher_impl(PySSLSocket *self) return cipher_to_tuple(current); } -/*[clinic input] -_ssl._SSLSocket.getpeercertchain - der as binary_mode: bool = False - validate: bool = True -[clinic start generated code]*/ - static PyObject * -_ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, - int validate) -/*[clinic end generated code: output=8094e6d78d27eb9a input=f4dcd181d0d163eb]*/ +chain_to_pyobject(STACK_OF(X509) *peer_chain, int binary_mode) { int len, i; PyObject *retval = NULL, *ci=NULL; - STACK_OF(X509) *peer_chain; /* reference */ - - assert((self->ctx != NULL) && (self->ctx->ctx != NULL)); - if (self->ssl == NULL) { - Py_RETURN_NONE; - } - - /* The peer just transmits the intermediate cert chain EXCLUDING the root - * CA certificate as this side is suppose to have a copy of the root - * certificate for verification. */ - if (validate) { -#ifdef OPENSSL_VERSION_1_1 - peer_chain = SSL_get0_verified_chain(self->ssl); - long ret = SSL_get_verify_result(self->ssl); - if (ret != X509_V_OK) { -#ifdef SSL_R_CERTIFICATE_VERIFY_FAILED - long e = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED); -#else - long e = ERR_PACK(ERR_LIB_SSL, 0, 134); -#endif - fill_and_set_sslerror(self, PySSLCertVerificationErrorObject, PY_SSL_ERROR_SSL, NULL, __LINE__, e); - return NULL; - } -#else - PyErr_SetString( - PyExc_Exception, - "Getting verified certificate chains with SSL_get0_verified_chain" - " is only supported by OpenSSL 1.1.0 and later"); - return NULL; -#endif - } else { - peer_chain = SSL_get_peer_cert_chain(self->ssl); - if (self->socket_type == PY_SSL_SERVER) { - X509 *peer_cert = SSL_get_peer_certificate(self->ssl); - if (peer_cert != NULL) - sk_X509_insert(peer_chain, peer_cert, 0); - } - } if (peer_chain == NULL) { Py_RETURN_NONE; } len = sk_X509_num(peer_chain); - if ((retval = PyTuple_New(len)) == NULL) { return NULL; } @@ -2168,15 +2121,70 @@ _ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, if (ci == NULL) { Py_CLEAR(retval); - goto end; + break; } PyTuple_SET_ITEM(retval, i, ci); } - end: return retval; } +/*[clinic input] +_ssl._SSLSocket.get_peer_cert_chain + der as binary_mode: bool = False +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_get_peer_cert_chain_impl(PySSLSocket *self, int binary_mode) +/*[clinic end generated code: output=2fc1e5dce85798ba input=3dd8f43730febaa9]*/ +{ + STACK_OF(X509) *peer_chain; /* reference */ + + assert((self->ctx != NULL) && (self->ctx->ctx != NULL)); + if (self->ssl == NULL) + Py_RETURN_NONE; + + peer_chain = SSL_get_peer_cert_chain(self->ssl); + /* In OpenSSL only the client side includes the peer certificate. + * Manually add it if required it to be more consistent. */ + if (self->socket_type == PY_SSL_SERVER) { + X509 *peer_cert = SSL_get_peer_certificate(self->ssl); + if (peer_cert != NULL) { + if (peer_chain == NULL) + peer_chain = sk_X509_new_null(); + sk_X509_insert(peer_chain, peer_cert, 0); + } + } + return chain_to_pyobject(peer_chain, binary_mode); +} + +#ifdef OPENSSL_VERSION_1_1 +/*[clinic input] +_ssl._SSLSocket.get_verified_chain + der as binary_mode: bool = False +[clinic start generated code]*/ + +static PyObject * +_ssl__SSLSocket_get_verified_chain_impl(PySSLSocket *self, int binary_mode) +/*[clinic end generated code: output=6e07b709feaeb291 input=8f51efb220ed687f]*/ +{ + STACK_OF(X509) *peer_chain; /* reference */ + + assert((self->ctx != NULL) && (self->ctx->ctx != NULL)); + if (self->ssl == NULL) + Py_RETURN_NONE; + + peer_chain = SSL_get0_verified_chain(self->ssl); + long ret = SSL_get_verify_result(self->ssl); + if (ret != X509_V_OK) { + long e = ERR_PACK(ERR_LIB_SSL, 0, SSL_R_CERTIFICATE_VERIFY_FAILED); + fill_and_set_sslerror(self, PySSLCertVerificationErrorObject, PY_SSL_ERROR_SSL, NULL, __LINE__, e); + return NULL; + } + return chain_to_pyobject(peer_chain, binary_mode); +} +#endif + /*[clinic input] _ssl._SSLSocket.version [clinic start generated code]*/ @@ -3081,7 +3089,10 @@ static PyMethodDef PySSLMethods[] = { _SSL__SSLSOCKET_GET_CHANNEL_BINDING_METHODDEF _SSL__SSLSOCKET_CIPHER_METHODDEF _SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF - _SSL__SSLSOCKET_GETPEERCERTCHAIN_METHODDEF + _SSL__SSLSOCKET_GET_PEER_CERT_CHAIN_METHODDEF +#ifdef OPENSSL_VERSION_1_1 + _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF +#endif _SSL__SSLSOCKET_VERSION_METHODDEF _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF _SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 41f6fcd4c3bc9a..2ff51b0947d18c 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -122,56 +122,88 @@ _ssl__SSLSocket_cipher(PySSLSocket *self, PyObject *Py_UNUSED(ignored)) return _ssl__SSLSocket_cipher_impl(self); } -PyDoc_STRVAR(_ssl__SSLSocket_getpeercertchain__doc__, -"getpeercertchain($self, /, der=False, validate=True)\n" +PyDoc_STRVAR(_ssl__SSLSocket_get_peer_cert_chain__doc__, +"get_peer_cert_chain($self, /, der=False)\n" "--\n" "\n"); -#define _SSL__SSLSOCKET_GETPEERCERTCHAIN_METHODDEF \ - {"getpeercertchain", (PyCFunction)(void(*)(void))_ssl__SSLSocket_getpeercertchain, METH_FASTCALL|METH_KEYWORDS, _ssl__SSLSocket_getpeercertchain__doc__}, +#define _SSL__SSLSOCKET_GET_PEER_CERT_CHAIN_METHODDEF \ + {"get_peer_cert_chain", (PyCFunction)(void(*)(void))_ssl__SSLSocket_get_peer_cert_chain, METH_FASTCALL|METH_KEYWORDS, _ssl__SSLSocket_get_peer_cert_chain__doc__}, static PyObject * -_ssl__SSLSocket_getpeercertchain_impl(PySSLSocket *self, int binary_mode, - int validate); +_ssl__SSLSocket_get_peer_cert_chain_impl(PySSLSocket *self, int binary_mode); static PyObject * -_ssl__SSLSocket_getpeercertchain(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_ssl__SSLSocket_get_peer_cert_chain(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"der", "validate", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "getpeercertchain", 0}; - PyObject *argsbuf[2]; + static const char * const _keywords[] = {"der", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "get_peer_cert_chain", 0}; + PyObject *argsbuf[1]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int binary_mode = 0; - int validate = 1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); if (!args) { goto exit; } if (!noptargs) { goto skip_optional_pos; } - if (args[0]) { - binary_mode = PyObject_IsTrue(args[0]); - if (binary_mode < 0) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } + binary_mode = PyObject_IsTrue(args[0]); + if (binary_mode < 0) { + goto exit; } - validate = PyObject_IsTrue(args[1]); - if (validate < 0) { +skip_optional_pos: + return_value = _ssl__SSLSocket_get_peer_cert_chain_impl(self, binary_mode); + +exit: + return return_value; +} + +#if defined(OPENSSL_VERSION_1_1) + +PyDoc_STRVAR(_ssl__SSLSocket_get_verified_chain__doc__, +"get_verified_chain($self, /, der=False)\n" +"--\n" +"\n"); + +#define _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF \ + {"get_verified_chain", (PyCFunction)(void(*)(void))_ssl__SSLSocket_get_verified_chain, METH_FASTCALL|METH_KEYWORDS, _ssl__SSLSocket_get_verified_chain__doc__}, + +static PyObject * +_ssl__SSLSocket_get_verified_chain_impl(PySSLSocket *self, int binary_mode); + +static PyObject * +_ssl__SSLSocket_get_verified_chain(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +{ + PyObject *return_value = NULL; + static const char * const _keywords[] = {"der", NULL}; + static _PyArg_Parser _parser = {NULL, _keywords, "get_verified_chain", 0}; + PyObject *argsbuf[1]; + Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + int binary_mode = 0; + + args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf); + if (!args) { + goto exit; + } + if (!noptargs) { + goto skip_optional_pos; + } + binary_mode = PyObject_IsTrue(args[0]); + if (binary_mode < 0) { goto exit; } skip_optional_pos: - return_value = _ssl__SSLSocket_getpeercertchain_impl(self, binary_mode, validate); + return_value = _ssl__SSLSocket_get_verified_chain_impl(self, binary_mode); exit: return return_value; } +#endif /* defined(OPENSSL_VERSION_1_1) */ + PyDoc_STRVAR(_ssl__SSLSocket_version__doc__, "version($self, /)\n" "--\n" @@ -1470,6 +1502,10 @@ _ssl_enum_crls(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObje #endif /* defined(_MSC_VER) */ +#ifndef _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF + #define _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF +#endif /* !defined(_SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF) */ + #ifndef _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF #define _SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF #endif /* !defined(_SSL__SSLSOCKET_SELECTED_NPN_PROTOCOL_METHODDEF) */ @@ -1497,4 +1533,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=7898c106854115f2 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=db4f0808302f242f input=a9049054013a1b77]*/ From 1f8dfd64612194274e411606af443b33c843f679 Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Fri, 31 Jan 2020 17:37:38 +0100 Subject: [PATCH 6/7] bpo-18233: Rename SSLSocket.get_peer_cert_chain to SSLSocket.get_unverified_chain --- Doc/library/ssl.rst | 2 +- Lib/ssl.py | 8 ++++---- Lib/test/test_ssl.py | 6 +++--- .../2020-01-10-16-06-13.bpo-18233.EsqH1K.rst | 2 +- Modules/_ssl.c | 8 ++++---- Modules/clinic/_ssl.c.h | 18 +++++++++--------- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 976cbbe4d6ef09..46e12496ddbf10 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1259,7 +1259,7 @@ SSL sockets also have the following additional methods and attributes: .. versionchanged:: 3.9 IPv6 address strings no longer have a trailing new line. -.. method:: SSLSocket.get_peer_cert_chain(binary_form=False) +.. method:: SSLSocket.get_unverified_chain(binary_form=False) Returns an **unverified** certificate chain for the peer. If no chain is provided, returns :const:`None`. Otherwise returns a tuple of dicts diff --git a/Lib/ssl.py b/Lib/ssl.py index 1c0d2c510d3500..b96f8172a2c50d 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -905,12 +905,12 @@ def getpeercert(self, binary_form=False): """ return self._sslobj.getpeercert(binary_form) - def get_peer_cert_chain(self, binary_form=False): + def get_unverified_chain(self, binary_form=False): """"Returns the certificate chain of the SSL connection as a tuple of dicts. It is *not* a verified chain. Return ``None`` if no chain is provided.""" - return self._sslobj.get_peer_cert_chain(binary_form) + return self._sslobj.get_unverified_chain(binary_form) if hasattr(_ssl._SSLSocket, 'get_verified_chain'): def get_verified_chain(self, binary_form=False): @@ -1139,10 +1139,10 @@ def getpeercert(self, binary_form=False): return self._sslobj.getpeercert(binary_form) @_sslcopydoc - def get_peer_cert_chain(self, binary_form=False): + def get_unverified_chain(self, binary_form=False): self._checkClosed() self._check_connected() - return self._sslobj.get_peer_cert_chain(binary_form) + return self._sslobj.get_unverified_chain(binary_form) if hasattr(_ssl._SSLSocket, 'get_verified_chain'): @_sslcopydoc diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index c8b2dea29b467e..2fbffeb1840c6f 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -2160,7 +2160,7 @@ def test_get_ca_certs_capath(self): self.assertTrue(cert) self.assertEqual(len(ctx.get_ca_certs()), 1) - def test_get_peer_cert_chain(self): + def test_get_unverified_chain(self): ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) ctx.verify_mode = ssl.CERT_REQUIRED ctx.load_verify_locations(capath=CAPATH) @@ -2169,8 +2169,8 @@ def test_get_peer_cert_chain(self): try: peer_cert = s.getpeercert() peer_cert_bin = s.getpeercert(True) - chain_no_validate = s.get_peer_cert_chain() - chain_bin_no_validate = s.get_peer_cert_chain(True) + chain_no_validate = s.get_unverified_chain() + chain_bin_no_validate = s.get_unverified_chain(True) finally: self.assertTrue(peer_cert) self.assertTrue(peer_cert_bin) diff --git a/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst b/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst index 956deb0b9686de..25c5280aaae954 100644 --- a/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst +++ b/Misc/NEWS.d/next/Library/2020-01-10-16-06-13.bpo-18233.EsqH1K.rst @@ -1 +1 @@ -Add :meth:`ssl.SSLSocket.get_peer_cert_chain` and :meth:`ssl.SSLSocket.get_verified_chain` for accessing the certificate chain of SSL connections. +Add :meth:`ssl.SSLSocket.get_unverified_chain` and :meth:`ssl.SSLSocket.get_verified_chain` for accessing the certificate chain of SSL connections. diff --git a/Modules/_ssl.c b/Modules/_ssl.c index b7966bca14c2fa..5c74984aa51cfa 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -2130,13 +2130,13 @@ chain_to_pyobject(STACK_OF(X509) *peer_chain, int binary_mode) } /*[clinic input] -_ssl._SSLSocket.get_peer_cert_chain +_ssl._SSLSocket.get_unverified_chain der as binary_mode: bool = False [clinic start generated code]*/ static PyObject * -_ssl__SSLSocket_get_peer_cert_chain_impl(PySSLSocket *self, int binary_mode) -/*[clinic end generated code: output=2fc1e5dce85798ba input=3dd8f43730febaa9]*/ +_ssl__SSLSocket_get_unverified_chain_impl(PySSLSocket *self, int binary_mode) +/*[clinic end generated code: output=a84c4e7bb50f3477 input=842931a7d60f135e]*/ { STACK_OF(X509) *peer_chain; /* reference */ @@ -3089,7 +3089,7 @@ static PyMethodDef PySSLMethods[] = { _SSL__SSLSOCKET_GET_CHANNEL_BINDING_METHODDEF _SSL__SSLSOCKET_CIPHER_METHODDEF _SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF - _SSL__SSLSOCKET_GET_PEER_CERT_CHAIN_METHODDEF + _SSL__SSLSOCKET_GET_UNVERIFIED_CHAIN_METHODDEF #ifdef OPENSSL_VERSION_1_1 _SSL__SSLSOCKET_GET_VERIFIED_CHAIN_METHODDEF #endif diff --git a/Modules/clinic/_ssl.c.h b/Modules/clinic/_ssl.c.h index 2ff51b0947d18c..7d1c72c910596f 100644 --- a/Modules/clinic/_ssl.c.h +++ b/Modules/clinic/_ssl.c.h @@ -122,23 +122,23 @@ _ssl__SSLSocket_cipher(PySSLSocket *self, PyObject *Py_UNUSED(ignored)) return _ssl__SSLSocket_cipher_impl(self); } -PyDoc_STRVAR(_ssl__SSLSocket_get_peer_cert_chain__doc__, -"get_peer_cert_chain($self, /, der=False)\n" +PyDoc_STRVAR(_ssl__SSLSocket_get_unverified_chain__doc__, +"get_unverified_chain($self, /, der=False)\n" "--\n" "\n"); -#define _SSL__SSLSOCKET_GET_PEER_CERT_CHAIN_METHODDEF \ - {"get_peer_cert_chain", (PyCFunction)(void(*)(void))_ssl__SSLSocket_get_peer_cert_chain, METH_FASTCALL|METH_KEYWORDS, _ssl__SSLSocket_get_peer_cert_chain__doc__}, +#define _SSL__SSLSOCKET_GET_UNVERIFIED_CHAIN_METHODDEF \ + {"get_unverified_chain", (PyCFunction)(void(*)(void))_ssl__SSLSocket_get_unverified_chain, METH_FASTCALL|METH_KEYWORDS, _ssl__SSLSocket_get_unverified_chain__doc__}, static PyObject * -_ssl__SSLSocket_get_peer_cert_chain_impl(PySSLSocket *self, int binary_mode); +_ssl__SSLSocket_get_unverified_chain_impl(PySSLSocket *self, int binary_mode); static PyObject * -_ssl__SSLSocket_get_peer_cert_chain(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +_ssl__SSLSocket_get_unverified_chain(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { PyObject *return_value = NULL; static const char * const _keywords[] = {"der", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "get_peer_cert_chain", 0}; + static _PyArg_Parser _parser = {NULL, _keywords, "get_unverified_chain", 0}; PyObject *argsbuf[1]; Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; int binary_mode = 0; @@ -155,7 +155,7 @@ _ssl__SSLSocket_get_peer_cert_chain(PySSLSocket *self, PyObject *const *args, Py goto exit; } skip_optional_pos: - return_value = _ssl__SSLSocket_get_peer_cert_chain_impl(self, binary_mode); + return_value = _ssl__SSLSocket_get_unverified_chain_impl(self, binary_mode); exit: return return_value; @@ -1533,4 +1533,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=db4f0808302f242f input=a9049054013a1b77]*/ +/*[clinic end generated code: output=3f880c7260e778fd input=a9049054013a1b77]*/ From dd39c49482832ced5e56c1d1bce27e819d7c290a Mon Sep 17 00:00:00 2001 From: Chris Burr Date: Wed, 9 Dec 2020 15:42:39 +0100 Subject: [PATCH 7/7] bpo-18233: Update added in version to 3.10 --- Doc/library/ssl.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index 46e12496ddbf10..4ad63bed501735 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -1271,7 +1271,7 @@ SSL sockets also have the following additional methods and attributes: this method returns a tuple with each element corresponding to the DER-encoded form of the entire certificate as a sequence of bytes. - .. versionadded:: 3.9 + .. versionadded:: 3.10 .. warning:: This is not a verified chain. See :meth:`ssl.SSLSocket.get_verified_chain`. @@ -1288,7 +1288,7 @@ SSL sockets also have the following additional methods and attributes: this method returns a tuple with each element corresponding to the DER-encoded form of the entire certificate as a sequence of bytes. - .. versionadded:: 3.9 + .. versionadded:: 3.10 .. note:: This features requires OpenSSL 1.1.0 or newer. pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy