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.