Commit 141c5e8c authored by Christian Heimes's avatar Christian Heimes Committed by GitHub

bpo-24334: Cleanup SSLSocket (#5252)

* The SSLSocket is no longer implemented on top of SSLObject to
  avoid an extra level of indirection.
* Owner and session are now handled in the internal constructor.
* _ssl._SSLSocket now uses the same method names as SSLSocket and
  SSLObject.
* Channel binding type check is now handled in C code. Channel binding
  is always available.

The patch also changes the signature of SSLObject.__init__(). In my
opinion it's fine. A SSLObject is not a user-constructable object.
SSLContext.wrap_bio() is the only valid factory.
parent b18f8bc1
This diff is collapsed.
......@@ -455,6 +455,8 @@ class BasicSocketTests(unittest.TestCase):
self.assertRaises(OSError, ss.recvfrom_into, bytearray(b'x'), 1)
self.assertRaises(OSError, ss.send, b'x')
self.assertRaises(OSError, ss.sendto, b'x', ('0.0.0.0', 0))
self.assertRaises(NotImplementedError, ss.sendmsg,
[b'x'], (), 0, ('0.0.0.0', 0))
def test_timeout(self):
# Issue #8524: when creating an SSL socket, the timeout of the
......@@ -3381,11 +3383,13 @@ class ThreadedTests(unittest.TestCase):
chatty=False) as server:
with context.wrap_socket(socket.socket()) as s:
self.assertIs(s.version(), None)
self.assertIs(s._sslobj, None)
s.connect((HOST, server.port))
if ssl.OPENSSL_VERSION_INFO >= (1, 0, 2):
self.assertEqual(s.version(), 'TLSv1.2')
else: # 0.9.8 to 1.0.1
self.assertIn(s.version(), ('TLSv1', 'TLSv1.2'))
self.assertIs(s._sslobj, None)
self.assertIs(s.version(), None)
@unittest.skipUnless(ssl.HAS_TLSv1_3,
......
Internal implementation details of ssl module were cleaned up. The SSLSocket
has one less layer of indirection. Owner and session information are now
handled by the SSLSocket and SSLObject constructor. Channel binding
implementation has been simplified.
......@@ -408,6 +408,8 @@ class _ssl.SSLSession "PySSLSession *" "&PySSLSession_Type"
static int PySSL_select(PySocketSockObject *s, int writing, _PyTime_t timeout);
static int PySSL_set_owner(PySSLSocket *, PyObject *, void *);
static int PySSL_set_session(PySSLSocket *, PyObject *, void *);
#define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type)
#define PySSLMemoryBIO_Check(v) (Py_TYPE(v) == &PySSLMemoryBIO_Type)
#define PySSLSession_Check(v) (Py_TYPE(v) == &PySSLSession_Type)
......@@ -799,6 +801,7 @@ static PySSLSocket *
newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
enum py_ssl_server_or_client socket_type,
char *server_hostname,
PyObject *owner, PyObject *session,
PySSLMemoryBIO *inbio, PySSLMemoryBIO *outbio)
{
PySSLSocket *self;
......@@ -875,6 +878,18 @@ newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock,
return NULL;
}
}
if (owner && owner != Py_None) {
if (PySSL_set_owner(self, owner, NULL) == -1) {
Py_DECREF(self);
return NULL;
}
}
if (session && session != Py_None) {
if (PySSL_set_session(self, session, NULL) == -1) {
Py_DECREF(self);
return NULL;
}
}
return self;
}
......@@ -1677,7 +1692,7 @@ _ssl__test_decode_cert_impl(PyObject *module, PyObject *path)
/*[clinic input]
_ssl._SSLSocket.peer_certificate
_ssl._SSLSocket.getpeercert
der as binary_mode: bool = False
/
......@@ -1693,8 +1708,8 @@ return the certificate even if it wasn't validated.
[clinic start generated code]*/
static PyObject *
_ssl__SSLSocket_peer_certificate_impl(PySSLSocket *self, int binary_mode)
/*[clinic end generated code: output=f0dc3e4d1d818a1d input=8281bd1d193db843]*/
_ssl__SSLSocket_getpeercert_impl(PySSLSocket *self, int binary_mode)
/*[clinic end generated code: output=1f0ab66dfb693c88 input=c0fbe802e57629b7]*/
{
int verification;
X509 *peer_cert;
......@@ -2395,13 +2410,11 @@ error:
_ssl._SSLSocket.shutdown
Does the SSL shutdown handshake with the remote end.
Returns the underlying socket object.
[clinic start generated code]*/
static PyObject *
_ssl__SSLSocket_shutdown_impl(PySSLSocket *self)
/*[clinic end generated code: output=ca1aa7ed9d25ca42 input=ede2cc1a2ddf0ee4]*/
/*[clinic end generated code: output=ca1aa7ed9d25ca42 input=11d39e69b0a2bf4a]*/
{
int err, sockstate, nonblocking;
int zeros = 0;
......@@ -2506,37 +2519,48 @@ error:
}
/*[clinic input]
_ssl._SSLSocket.tls_unique_cb
_ssl._SSLSocket.get_channel_binding
cb_type: str = "tls-unique"
Returns the 'tls-unique' channel binding data, as defined by RFC 5929.
Get channel binding data for current connection.
If the TLS handshake is not yet complete, None is returned.
Raise ValueError if the requested `cb_type` is not supported. Return bytes
of the data or None if the data is not available (e.g. before the handshake).
Only 'tls-unique' channel binding data from RFC 5929 is supported.
[clinic start generated code]*/
static PyObject *
_ssl__SSLSocket_tls_unique_cb_impl(PySSLSocket *self)
/*[clinic end generated code: output=f3a832d603f586af input=439525c7b3d8d34d]*/
_ssl__SSLSocket_get_channel_binding_impl(PySSLSocket *self,
const char *cb_type)
/*[clinic end generated code: output=34bac9acb6a61d31 input=08b7e43b99c17d41]*/
{
PyObject *retval = NULL;
char buf[PySSL_CB_MAXLEN];
size_t len;
if (SSL_session_reused(self->ssl) ^ !self->socket_type) {
/* if session is resumed XOR we are the client */
len = SSL_get_finished(self->ssl, buf, PySSL_CB_MAXLEN);
if (strcmp(cb_type, "tls-unique") == 0) {
if (SSL_session_reused(self->ssl) ^ !self->socket_type) {
/* if session is resumed XOR we are the client */
len = SSL_get_finished(self->ssl, buf, PySSL_CB_MAXLEN);
}
else {
/* if a new session XOR we are the server */
len = SSL_get_peer_finished(self->ssl, buf, PySSL_CB_MAXLEN);
}
}
else {
/* if a new session XOR we are the server */
len = SSL_get_peer_finished(self->ssl, buf, PySSL_CB_MAXLEN);
PyErr_Format(
PyExc_ValueError,
"'%s' channel binding type not implemented",
cb_type
);
return NULL;
}
/* It cannot be negative in current OpenSSL version as of July 2011 */
if (len == 0)
Py_RETURN_NONE;
retval = PyBytes_FromStringAndSize(buf, len);
return retval;
return PyBytes_FromStringAndSize(buf, len);
}
#ifdef OPENSSL_VERSION_1_1
......@@ -2706,7 +2730,8 @@ static PyMethodDef PySSLMethods[] = {
_SSL__SSLSOCKET_WRITE_METHODDEF
_SSL__SSLSOCKET_READ_METHODDEF
_SSL__SSLSOCKET_PENDING_METHODDEF
_SSL__SSLSOCKET_PEER_CERTIFICATE_METHODDEF
_SSL__SSLSOCKET_GETPEERCERT_METHODDEF
_SSL__SSLSOCKET_GET_CHANNEL_BINDING_METHODDEF
_SSL__SSLSOCKET_CIPHER_METHODDEF
_SSL__SSLSOCKET_SHARED_CIPHERS_METHODDEF
_SSL__SSLSOCKET_VERSION_METHODDEF
......@@ -2714,7 +2739,6 @@ static PyMethodDef PySSLMethods[] = {
_SSL__SSLSOCKET_SELECTED_ALPN_PROTOCOL_METHODDEF
_SSL__SSLSOCKET_COMPRESSION_METHODDEF
_SSL__SSLSOCKET_SHUTDOWN_METHODDEF
_SSL__SSLSOCKET_TLS_UNIQUE_CB_METHODDEF
{NULL, NULL}
};
......@@ -3810,13 +3834,17 @@ _ssl._SSLContext._wrap_socket
sock: object(subclass_of="PySocketModule.Sock_Type")
server_side: int
server_hostname as hostname_obj: object = None
*
owner: object = None
session: object = None
[clinic start generated code]*/
static PyObject *
_ssl__SSLContext__wrap_socket_impl(PySSLContext *self, PyObject *sock,
int server_side, PyObject *hostname_obj)
/*[clinic end generated code: output=6973e4b60995e933 input=83859b9156ddfc63]*/
int server_side, PyObject *hostname_obj,
PyObject *owner, PyObject *session)
/*[clinic end generated code: output=f103f238633940b4 input=957d5006183d1894]*/
{
char *hostname = NULL;
PyObject *res;
......@@ -3830,6 +3858,7 @@ _ssl__SSLContext__wrap_socket_impl(PySSLContext *self, PyObject *sock,
res = (PyObject *) newPySSLSocket(self, (PySocketSockObject *)sock,
server_side, hostname,
owner, session,
NULL, NULL);
if (hostname != NULL)
PyMem_Free(hostname);
......@@ -3842,14 +3871,18 @@ _ssl._SSLContext._wrap_bio
outgoing: object(subclass_of="&PySSLMemoryBIO_Type", type="PySSLMemoryBIO *")
server_side: int
server_hostname as hostname_obj: object = None
*
owner: object = None
session: object = None
[clinic start generated code]*/
static PyObject *
_ssl__SSLContext__wrap_bio_impl(PySSLContext *self, PySSLMemoryBIO *incoming,
PySSLMemoryBIO *outgoing, int server_side,
PyObject *hostname_obj)
/*[clinic end generated code: output=4fe4ba75ad95940d input=17725ecdac0bf220]*/
PyObject *hostname_obj, PyObject *owner,
PyObject *session)
/*[clinic end generated code: output=5c5d6d9b41f99332 input=8cf22f4d586ac56a]*/
{
char *hostname = NULL;
PyObject *res;
......@@ -3862,6 +3895,7 @@ _ssl__SSLContext__wrap_bio_impl(PySSLContext *self, PySSLMemoryBIO *incoming,
}
res = (PyObject *) newPySSLSocket(self, NULL, server_side, hostname,
owner, session,
incoming, outgoing);
PyMem_Free(hostname);
......@@ -5663,10 +5697,6 @@ PyInit__ssl(void)
Py_INCREF(r);
PyModule_AddObject(m, "HAS_SNI", r);
r = Py_True;
Py_INCREF(r);
PyModule_AddObject(m, "HAS_TLS_UNIQUE", r);
#ifdef OPENSSL_NO_ECDH
r = Py_False;
#else
......
......@@ -45,8 +45,8 @@ exit:
return return_value;
}
PyDoc_STRVAR(_ssl__SSLSocket_peer_certificate__doc__,
"peer_certificate($self, der=False, /)\n"
PyDoc_STRVAR(_ssl__SSLSocket_getpeercert__doc__,
"getpeercert($self, der=False, /)\n"
"--\n"
"\n"
"Returns the certificate for the peer.\n"
......@@ -59,23 +59,23 @@ PyDoc_STRVAR(_ssl__SSLSocket_peer_certificate__doc__,
"peer certificate, or None if no certificate was provided. This will\n"
"return the certificate even if it wasn\'t validated.");
#define _SSL__SSLSOCKET_PEER_CERTIFICATE_METHODDEF \
{"peer_certificate", (PyCFunction)_ssl__SSLSocket_peer_certificate, METH_FASTCALL, _ssl__SSLSocket_peer_certificate__doc__},
#define _SSL__SSLSOCKET_GETPEERCERT_METHODDEF \
{"getpeercert", (PyCFunction)_ssl__SSLSocket_getpeercert, METH_FASTCALL, _ssl__SSLSocket_getpeercert__doc__},
static PyObject *
_ssl__SSLSocket_peer_certificate_impl(PySSLSocket *self, int binary_mode);
_ssl__SSLSocket_getpeercert_impl(PySSLSocket *self, int binary_mode);
static PyObject *
_ssl__SSLSocket_peer_certificate(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs)
_ssl__SSLSocket_getpeercert(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
int binary_mode = 0;
if (!_PyArg_ParseStack(args, nargs, "|p:peer_certificate",
if (!_PyArg_ParseStack(args, nargs, "|p:getpeercert",
&binary_mode)) {
goto exit;
}
return_value = _ssl__SSLSocket_peer_certificate_impl(self, binary_mode);
return_value = _ssl__SSLSocket_getpeercert_impl(self, binary_mode);
exit:
return return_value;
......@@ -293,9 +293,7 @@ PyDoc_STRVAR(_ssl__SSLSocket_shutdown__doc__,
"shutdown($self, /)\n"
"--\n"
"\n"
"Does the SSL shutdown handshake with the remote end.\n"
"\n"
"Returns the underlying socket object.");
"Does the SSL shutdown handshake with the remote end.");
#define _SSL__SSLSOCKET_SHUTDOWN_METHODDEF \
{"shutdown", (PyCFunction)_ssl__SSLSocket_shutdown, METH_NOARGS, _ssl__SSLSocket_shutdown__doc__},
......@@ -309,24 +307,39 @@ _ssl__SSLSocket_shutdown(PySSLSocket *self, PyObject *Py_UNUSED(ignored))
return _ssl__SSLSocket_shutdown_impl(self);
}
PyDoc_STRVAR(_ssl__SSLSocket_tls_unique_cb__doc__,
"tls_unique_cb($self, /)\n"
PyDoc_STRVAR(_ssl__SSLSocket_get_channel_binding__doc__,
"get_channel_binding($self, /, cb_type=\'tls-unique\')\n"
"--\n"
"\n"
"Returns the \'tls-unique\' channel binding data, as defined by RFC 5929.\n"
"Get channel binding data for current connection.\n"
"\n"
"If the TLS handshake is not yet complete, None is returned.");
"Raise ValueError if the requested `cb_type` is not supported. Return bytes\n"
"of the data or None if the data is not available (e.g. before the handshake).\n"
"Only \'tls-unique\' channel binding data from RFC 5929 is supported.");
#define _SSL__SSLSOCKET_TLS_UNIQUE_CB_METHODDEF \
{"tls_unique_cb", (PyCFunction)_ssl__SSLSocket_tls_unique_cb, METH_NOARGS, _ssl__SSLSocket_tls_unique_cb__doc__},
#define _SSL__SSLSOCKET_GET_CHANNEL_BINDING_METHODDEF \
{"get_channel_binding", (PyCFunction)_ssl__SSLSocket_get_channel_binding, METH_FASTCALL|METH_KEYWORDS, _ssl__SSLSocket_get_channel_binding__doc__},
static PyObject *
_ssl__SSLSocket_tls_unique_cb_impl(PySSLSocket *self);
_ssl__SSLSocket_get_channel_binding_impl(PySSLSocket *self,
const char *cb_type);
static PyObject *
_ssl__SSLSocket_tls_unique_cb(PySSLSocket *self, PyObject *Py_UNUSED(ignored))
_ssl__SSLSocket_get_channel_binding(PySSLSocket *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
return _ssl__SSLSocket_tls_unique_cb_impl(self);
PyObject *return_value = NULL;
static const char * const _keywords[] = {"cb_type", NULL};
static _PyArg_Parser _parser = {"|s:get_channel_binding", _keywords, 0};
const char *cb_type = "tls-unique";
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&cb_type)) {
goto exit;
}
return_value = _ssl__SSLSocket_get_channel_binding_impl(self, cb_type);
exit:
return return_value;
}
static PyObject *
......@@ -538,7 +551,8 @@ PyDoc_STRVAR(_ssl__SSLContext_load_dh_params__doc__,
{"load_dh_params", (PyCFunction)_ssl__SSLContext_load_dh_params, METH_O, _ssl__SSLContext_load_dh_params__doc__},
PyDoc_STRVAR(_ssl__SSLContext__wrap_socket__doc__,
"_wrap_socket($self, /, sock, server_side, server_hostname=None)\n"
"_wrap_socket($self, /, sock, server_side, server_hostname=None, *,\n"
" owner=None, session=None)\n"
"--\n"
"\n");
......@@ -547,23 +561,26 @@ PyDoc_STRVAR(_ssl__SSLContext__wrap_socket__doc__,
static PyObject *
_ssl__SSLContext__wrap_socket_impl(PySSLContext *self, PyObject *sock,
int server_side, PyObject *hostname_obj);
int server_side, PyObject *hostname_obj,
PyObject *owner, PyObject *session);
static PyObject *
_ssl__SSLContext__wrap_socket(PySSLContext *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"sock", "server_side", "server_hostname", NULL};
static _PyArg_Parser _parser = {"O!i|O:_wrap_socket", _keywords, 0};
static const char * const _keywords[] = {"sock", "server_side", "server_hostname", "owner", "session", NULL};
static _PyArg_Parser _parser = {"O!i|O$OO:_wrap_socket", _keywords, 0};
PyObject *sock;
int server_side;
PyObject *hostname_obj = Py_None;
PyObject *owner = Py_None;
PyObject *session = Py_None;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
PySocketModule.Sock_Type, &sock, &server_side, &hostname_obj)) {
PySocketModule.Sock_Type, &sock, &server_side, &hostname_obj, &owner, &session)) {
goto exit;
}
return_value = _ssl__SSLContext__wrap_socket_impl(self, sock, server_side, hostname_obj);
return_value = _ssl__SSLContext__wrap_socket_impl(self, sock, server_side, hostname_obj, owner, session);
exit:
return return_value;
......@@ -571,7 +588,7 @@ exit:
PyDoc_STRVAR(_ssl__SSLContext__wrap_bio__doc__,
"_wrap_bio($self, /, incoming, outgoing, server_side,\n"
" server_hostname=None)\n"
" server_hostname=None, *, owner=None, session=None)\n"
"--\n"
"\n");
......@@ -581,24 +598,27 @@ PyDoc_STRVAR(_ssl__SSLContext__wrap_bio__doc__,
static PyObject *
_ssl__SSLContext__wrap_bio_impl(PySSLContext *self, PySSLMemoryBIO *incoming,
PySSLMemoryBIO *outgoing, int server_side,
PyObject *hostname_obj);
PyObject *hostname_obj, PyObject *owner,
PyObject *session);
static PyObject *
_ssl__SSLContext__wrap_bio(PySSLContext *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
static const char * const _keywords[] = {"incoming", "outgoing", "server_side", "server_hostname", NULL};
static _PyArg_Parser _parser = {"O!O!i|O:_wrap_bio", _keywords, 0};
static const char * const _keywords[] = {"incoming", "outgoing", "server_side", "server_hostname", "owner", "session", NULL};
static _PyArg_Parser _parser = {"O!O!i|O$OO:_wrap_bio", _keywords, 0};
PySSLMemoryBIO *incoming;
PySSLMemoryBIO *outgoing;
int server_side;
PyObject *hostname_obj = Py_None;
PyObject *owner = Py_None;
PyObject *session = Py_None;
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
&PySSLMemoryBIO_Type, &incoming, &PySSLMemoryBIO_Type, &outgoing, &server_side, &hostname_obj)) {
&PySSLMemoryBIO_Type, &incoming, &PySSLMemoryBIO_Type, &outgoing, &server_side, &hostname_obj, &owner, &session)) {
goto exit;
}
return_value = _ssl__SSLContext__wrap_bio_impl(self, incoming, outgoing, server_side, hostname_obj);
return_value = _ssl__SSLContext__wrap_bio_impl(self, incoming, outgoing, server_side, hostname_obj, owner, session);
exit:
return return_value;
......@@ -1155,4 +1175,4 @@ exit:
#ifndef _SSL_ENUM_CRLS_METHODDEF
#define _SSL_ENUM_CRLS_METHODDEF
#endif /* !defined(_SSL_ENUM_CRLS_METHODDEF) */
/*[clinic end generated code: output=84e1fd89aff9b0f7 input=a9049054013a1b77]*/
/*[clinic end generated code: output=d987411caeb106e7 input=a9049054013a1b77]*/
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment