mirror of
https://github.com/python/cpython.git
synced 2025-12-07 17:57:56 +00:00
gh-96931: Fix incorrect results in ssl.SSLSocket.shared_ciphers (#96932)
This commit is contained in:
parent
ea93bde4ec
commit
af9c34f6ef
4 changed files with 33 additions and 12 deletions
|
|
@ -1218,7 +1218,7 @@ SSL sockets also have the following additional methods and attributes:
|
||||||
|
|
||||||
.. method:: SSLSocket.shared_ciphers()
|
.. method:: SSLSocket.shared_ciphers()
|
||||||
|
|
||||||
Return the list of ciphers shared by the client during the handshake. Each
|
Return the list of ciphers available in both the client and server. Each
|
||||||
entry of the returned list is a three-value tuple containing the name of the
|
entry of the returned list is a three-value tuple containing the name of the
|
||||||
cipher, the version of the SSL protocol that defines its use, and the number
|
cipher, the version of the SSL protocol that defines its use, and the number
|
||||||
of secret bits the cipher uses. :meth:`~SSLSocket.shared_ciphers` returns
|
of secret bits the cipher uses. :meth:`~SSLSocket.shared_ciphers` returns
|
||||||
|
|
|
||||||
|
|
@ -2082,13 +2082,13 @@ class SimpleBackgroundTests(unittest.TestCase):
|
||||||
self.assertIs(sslobj._sslobj.owner, sslobj)
|
self.assertIs(sslobj._sslobj.owner, sslobj)
|
||||||
self.assertIsNone(sslobj.cipher())
|
self.assertIsNone(sslobj.cipher())
|
||||||
self.assertIsNone(sslobj.version())
|
self.assertIsNone(sslobj.version())
|
||||||
self.assertIsNotNone(sslobj.shared_ciphers())
|
self.assertIsNone(sslobj.shared_ciphers())
|
||||||
self.assertRaises(ValueError, sslobj.getpeercert)
|
self.assertRaises(ValueError, sslobj.getpeercert)
|
||||||
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
|
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
|
||||||
self.assertIsNone(sslobj.get_channel_binding('tls-unique'))
|
self.assertIsNone(sslobj.get_channel_binding('tls-unique'))
|
||||||
self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
|
self.ssl_io_loop(sock, incoming, outgoing, sslobj.do_handshake)
|
||||||
self.assertTrue(sslobj.cipher())
|
self.assertTrue(sslobj.cipher())
|
||||||
self.assertIsNotNone(sslobj.shared_ciphers())
|
self.assertIsNone(sslobj.shared_ciphers())
|
||||||
self.assertIsNotNone(sslobj.version())
|
self.assertIsNotNone(sslobj.version())
|
||||||
self.assertTrue(sslobj.getpeercert())
|
self.assertTrue(sslobj.getpeercert())
|
||||||
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
|
if 'tls-unique' in ssl.CHANNEL_BINDING_TYPES:
|
||||||
|
|
@ -4051,7 +4051,7 @@ class ThreadedTests(unittest.TestCase):
|
||||||
def test_shared_ciphers(self):
|
def test_shared_ciphers(self):
|
||||||
client_context, server_context, hostname = testing_context()
|
client_context, server_context, hostname = testing_context()
|
||||||
client_context.set_ciphers("AES128:AES256")
|
client_context.set_ciphers("AES128:AES256")
|
||||||
server_context.set_ciphers("AES256")
|
server_context.set_ciphers("AES256:eNULL")
|
||||||
expected_algs = [
|
expected_algs = [
|
||||||
"AES256", "AES-256",
|
"AES256", "AES-256",
|
||||||
# TLS 1.3 ciphers are always enabled
|
# TLS 1.3 ciphers are always enabled
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
Fix incorrect results from :meth:`ssl.SSLSocket.shared_ciphers`
|
||||||
|
|
@ -1998,24 +1998,44 @@ static PyObject *
|
||||||
_ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self)
|
_ssl__SSLSocket_shared_ciphers_impl(PySSLSocket *self)
|
||||||
/*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/
|
/*[clinic end generated code: output=3d174ead2e42c4fd input=0bfe149da8fe6306]*/
|
||||||
{
|
{
|
||||||
STACK_OF(SSL_CIPHER) *ciphers;
|
STACK_OF(SSL_CIPHER) *server_ciphers;
|
||||||
int i;
|
STACK_OF(SSL_CIPHER) *client_ciphers;
|
||||||
|
int i, len;
|
||||||
PyObject *res;
|
PyObject *res;
|
||||||
|
const SSL_CIPHER* cipher;
|
||||||
|
|
||||||
ciphers = SSL_get_ciphers(self->ssl);
|
/* Rather than use SSL_get_shared_ciphers, we use an equivalent algorithm because:
|
||||||
if (!ciphers)
|
|
||||||
|
1) It returns a colon seperated list of strings, in an undefined
|
||||||
|
order, that we would have to post process back into tuples.
|
||||||
|
2) It will return a truncated string with no indication that it has
|
||||||
|
done so, if the buffer is too small.
|
||||||
|
*/
|
||||||
|
|
||||||
|
server_ciphers = SSL_get_ciphers(self->ssl);
|
||||||
|
if (!server_ciphers)
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
res = PyList_New(sk_SSL_CIPHER_num(ciphers));
|
client_ciphers = SSL_get_client_ciphers(self->ssl);
|
||||||
|
if (!client_ciphers)
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
|
||||||
|
res = PyList_New(sk_SSL_CIPHER_num(server_ciphers));
|
||||||
if (!res)
|
if (!res)
|
||||||
return NULL;
|
return NULL;
|
||||||
for (i = 0; i < sk_SSL_CIPHER_num(ciphers); i++) {
|
len = 0;
|
||||||
PyObject *tup = cipher_to_tuple(sk_SSL_CIPHER_value(ciphers, i));
|
for (i = 0; i < sk_SSL_CIPHER_num(server_ciphers); i++) {
|
||||||
|
cipher = sk_SSL_CIPHER_value(server_ciphers, i);
|
||||||
|
if (sk_SSL_CIPHER_find(client_ciphers, cipher) < 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
PyObject *tup = cipher_to_tuple(cipher);
|
||||||
if (!tup) {
|
if (!tup) {
|
||||||
Py_DECREF(res);
|
Py_DECREF(res);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
PyList_SET_ITEM(res, i, tup);
|
PyList_SET_ITEM(res, len++, tup);
|
||||||
}
|
}
|
||||||
|
Py_SET_SIZE(res, len);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue