From e0fcf51edcddac12ed57fb5ef76bfa8f342b849a Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 13 Feb 2012 09:10:15 -0500 Subject: Minimal skeleton of a Python Session type --- OpenSSL/ssl/session.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ OpenSSL/ssl/session.h | 25 ++++++++ OpenSSL/ssl/ssl.c | 2 + OpenSSL/ssl/ssl.h | 1 + OpenSSL/test/test_ssl.py | 27 ++++++++- setup.py | 4 +- 6 files changed, 202 insertions(+), 3 deletions(-) create mode 100644 OpenSSL/ssl/session.c create mode 100644 OpenSSL/ssl/session.h diff --git a/OpenSSL/ssl/session.c b/OpenSSL/ssl/session.c new file mode 100644 index 0000000..5cd23b6 --- /dev/null +++ b/OpenSSL/ssl/session.c @@ -0,0 +1,146 @@ +/* + * session.c + * + * Copyright (C) Jean-Paul Calderone + * Copyright (C) Alejandro Alvarez Ayllon + * See LICENSE for details. + * + * SSL Session object data structures and functions. + * + */ +#include +#define SSL_MODULE +#include "ssl.h" + +static char ssl_Session_doc[] = "\n\ +Session() -> Session instance\n\ +\n\ +"; + +/* + * Initialize an already-constructed Session instance. + */ +static ssl_SessionObj *ssl_Session_init(ssl_SessionObj *self) { + /* + self->sess = d2i_SSL_SESSION(NULL, &buffer, len); + + if (!self->sess) { + exception_from_error_queue(ssl_Error); + return NULL; + } + */ + return self; + +} + + +/* + * Create a Session object + */ +static PyObject* +ssl_Session_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { + ssl_SessionObj *self; + + if (!PyArg_ParseTuple(args, ":Session")) { + return NULL; + } + + self = PyObject_New(ssl_SessionObj, &ssl_Session_Type); + if (self == NULL) { + return NULL; + } + + return (PyObject *)ssl_Session_init(self); +} + +/* + * Member methods in the Session object + * ADD_METHOD(name) expands to a correct PyMethodDef declaration + * { 'name', (PyCFunction)ssl_Session_name, METH_VARARGS } + * for convenience + * ADD_ALIAS(name,real) creates an "alias" of the ssl_Session_real + * function with the name 'name' + */ +#define ADD_METHOD(name) { #name, (PyCFunction)ssl_Session_##name, METH_VARARGS, ssl_Session_##name##_doc } +static PyMethodDef ssl_Session_methods[] = { +#if 0 + ADD_METHOD(asn1), + ADD_METHOD(get_time), + ADD_METHOD(get_timeout), +#ifdef SSL_SESSION_hash + ADD_METHOD(hash), +#endif + ADD_METHOD(set_time), + ADD_METHOD(set_timeout), +#endif + { NULL, NULL } +}; +#undef ADD_METHOD + +/* + * The Python Session type definition. + */ +PyTypeObject ssl_Session_Type = { + PyOpenSSL_HEAD_INIT(&PyType_Type, 0) + "OpenSSL.SSL.Session", + sizeof(ssl_SessionObj), + 0, + NULL, // (destructor)ssl_Session_dealloc, /* tp_dealloc */ + NULL, /* print */ + NULL, /* tp_getattr */ + NULL, /* setattr */ + NULL, /* compare */ + NULL, /* repr */ + NULL, /* as_number */ + NULL, /* as_sequence */ + NULL, /* as_mapping */ + NULL, /* hash */ + NULL, /* call */ + NULL, /* str */ + NULL, /* getattro */ + NULL, /* setattro */ + NULL, /* as_buffer */ + Py_TPFLAGS_DEFAULT, // Py_TPFLAGS_HAVE_GC, /* tp_flags */ + ssl_Session_doc, /* tp_doc */ + NULL, // (traverseproc)ssl_Session_traverse, /* tp_traverse */ + NULL, // (inquiry)ssl_Session_clear, /* tp_clear */ + NULL, /* tp_richcompare */ + 0, /* tp_weaklistoffset */ + NULL, /* tp_iter */ + NULL, /* tp_iternext */ + ssl_Session_methods, /* tp_methods */ + NULL, /* tp_members */ + NULL, /* tp_getset */ + NULL, /* tp_base */ + NULL, /* tp_dict */ + NULL, /* tp_descr_get */ + NULL, /* tp_descr_set */ + 0, /* tp_dictoffset */ + NULL, /* tp_init */ + NULL, /* tp_alloc */ + ssl_Session_new, /* tp_new */ +}; + +/* + * Initialize the Session part of the SSL sub module + * + * Arguments: dict - The OpenSSL.SSL module + * Returns: 1 for success, 0 otherwise + */ +int +init_ssl_session(PyObject *module) { + + if (PyType_Ready(&ssl_Session_Type) < 0) { + return 0; + } + + /* PyModule_AddObject steals a reference. + */ + Py_INCREF((PyObject *)&ssl_Session_Type); + if (PyModule_AddObject(module, "Session", (PyObject *)&ssl_Session_Type) < 0) { + return 0; + } + + return 1; +} + diff --git a/OpenSSL/ssl/session.h b/OpenSSL/ssl/session.h new file mode 100644 index 0000000..98cee80 --- /dev/null +++ b/OpenSSL/ssl/session.h @@ -0,0 +1,25 @@ +/* + * session.h + * Copyright (C) Jean-Paul Calderone + * See LICENSE for details. + * + * Defined here is the Python type which represents an SSL session by wrapping + * an OpenSSL SSL_SESSION*. + * + */ + +#ifndef PyOpenSSL_SSL_SESSION_H_ +#define PyOpenSSL_SSL_SESSION_H_ + +#include +#include + +typedef struct { + PyObject_HEAD +} ssl_SessionObj; + +extern PyTypeObject ssl_Session_Type; + +extern int init_ssl_session(PyObject *); + +#endif diff --git a/OpenSSL/ssl/ssl.c b/OpenSSL/ssl/ssl.c index a68f447..5725d5d 100644 --- a/OpenSSL/ssl/ssl.c +++ b/OpenSSL/ssl/ssl.c @@ -298,6 +298,8 @@ do { \ if (!init_ssl_context(module)) goto error; + if (!init_ssl_session(module)) + goto error; if (!init_ssl_connection(module)) goto error; diff --git a/OpenSSL/ssl/ssl.h b/OpenSSL/ssl/ssl.h index 6a0a57e..3074ba5 100644 --- a/OpenSSL/ssl/ssl.h +++ b/OpenSSL/ssl/ssl.h @@ -16,6 +16,7 @@ #include #include #include "context.h" +#include "session.h" #include "connection.h" #include "../util.h" #include "../crypto/crypto.h" diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index cda6d53..529a454 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -34,7 +34,8 @@ from OpenSSL.SSL import ( from OpenSSL.SSL import ( Error, SysCallError, WantReadError, ZeroReturnError, SSLeay_version) -from OpenSSL.SSL import Context, ContextType, Connection, ConnectionType +from OpenSSL.SSL import ( + Context, ContextType, Session, Connection, ConnectionType) from OpenSSL.test.util import TestCase, bytes, b from OpenSSL.test.test_crypto import ( @@ -1046,6 +1047,30 @@ class ServerNameCallbackTests(TestCase, _LoopbackMixin): +class SessionTests(TestCase): + """ + Unit tests for :py:obj:`OpenSSL.SSL.Session`. + """ + def test_construction(self): + """ + :py:class:`Session` can be constructed with no arguments, creating a new + instance of that type. + """ + new_session = Session() + self.assertTrue(isinstance(new_session, Session)) + + + def test_construction_wrong_args(self): + """ + If any arguments are passed to :py:class:`Session`, :py:obj:`TypeError` + is raised. + """ + self.assertRaises(TypeError, Session, 123) + self.assertRaises(TypeError, Session, "hello") + self.assertRaises(TypeError, Session, object()) + + + class ConnectionTests(TestCase, _LoopbackMixin): """ Unit tests for :py:obj:`OpenSSL.SSL.Connection`. diff --git a/setup.py b/setup.py index 8441274..511c60c 100755 --- a/setup.py +++ b/setup.py @@ -34,9 +34,9 @@ crypto_dep = ['OpenSSL/crypto/crypto.h', 'OpenSSL/crypto/x509.h', rand_src = ['OpenSSL/rand/rand.c', 'OpenSSL/util.c'] rand_dep = ['OpenSSL/util.h'] ssl_src = ['OpenSSL/ssl/connection.c', 'OpenSSL/ssl/context.c', 'OpenSSL/ssl/ssl.c', - 'OpenSSL/util.c'] + 'OpenSSL/ssl/session.c', 'OpenSSL/util.c'] ssl_dep = ['OpenSSL/ssl/connection.h', 'OpenSSL/ssl/context.h', 'OpenSSL/ssl/ssl.h', - 'OpenSSL/util.h'] + 'OpenSSL/ssl/session.h', 'OpenSSL/util.h'] IncludeDirs = None LibraryDirs = None -- cgit v1.2.1 From 64eaffcc4524a6fa032a0510d4cd34a64c38e8c5 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Mon, 13 Feb 2012 11:53:49 -0500 Subject: Add Connection.get_session and have the Session object actually wrap an SSL_SESSION* (though there is actually not yet any way to tell that that is the case) --- OpenSSL/ssl/connection.c | 32 +++++++++++++++++++++++++ OpenSSL/ssl/session.c | 61 +++++++++++++++++++++++++++++------------------- OpenSSL/ssl/session.h | 2 ++ OpenSSL/test/test_ssl.py | 45 +++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 24 deletions(-) diff --git a/OpenSSL/ssl/connection.c b/OpenSSL/ssl/connection.c index 037f2a0..60887b0 100755 --- a/OpenSSL/ssl/connection.c +++ b/OpenSSL/ssl/connection.c @@ -1254,6 +1254,37 @@ ssl_Connection_want_write(ssl_ConnectionObj *self, PyObject *args) return PyLong_FromLong((long)SSL_want_write(self->ssl)); } +static char ssl_Connection_get_session_doc[] = "\n\ +Returns the Session currently used.\n\ +\n\ +@return: An instance of :py:class:`OpenSSL.SSL.Session` or :py:obj:`None` if\n\ + no session exists.\n\ +"; +static PyObject * +ssl_Connection_get_session(ssl_ConnectionObj *self, PyObject *args) { + ssl_SessionObj *session; + SSL_SESSION *native_session; + + if (!PyArg_ParseTuple(args, ":get_session")) { + return NULL; + } + + native_session = SSL_get1_session(self->ssl); + + if (native_session == NULL) { + Py_INCREF(Py_None); + return Py_None; + } + + session = ssl_Session_from_SSL_SESSION(native_session); + if (!session) { + Py_INCREF(Py_None); + return Py_None; + } + + return (PyObject*)session; +} + /* * Member methods in the Connection object * ADD_METHOD(name) expands to a correct PyMethodDef declaration @@ -1309,6 +1340,7 @@ static PyMethodDef ssl_Connection_methods[] = ADD_METHOD(want_write), ADD_METHOD(set_accept_state), ADD_METHOD(set_connect_state), + ADD_METHOD(get_session), { NULL, NULL } }; #undef ADD_ALIAS diff --git a/OpenSSL/ssl/session.c b/OpenSSL/ssl/session.c index 5cd23b6..fb9c83f 100644 --- a/OpenSSL/ssl/session.c +++ b/OpenSSL/ssl/session.c @@ -18,22 +18,15 @@ Session() -> Session instance\n\ "; /* - * Initialize an already-constructed Session instance. + * Initialize an already-constructed Session instance with an OpenSSL session + * structure (or NULL). A reference to the OpenSSL session structure is stolen. */ -static ssl_SessionObj *ssl_Session_init(ssl_SessionObj *self) { - /* - self->sess = d2i_SSL_SESSION(NULL, &buffer, len); - - if (!self->sess) { - exception_from_error_queue(ssl_Error); - return NULL; - } - */ +static ssl_SessionObj* +ssl_Session_init(ssl_SessionObj *self, SSL_SESSION *native_session) { + self->session = native_session; return self; - } - /* * Create a Session object */ @@ -50,7 +43,37 @@ ssl_Session_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { return NULL; } - return (PyObject *)ssl_Session_init(self); + return (PyObject *)ssl_Session_init(self, NULL); +} + +/* + * Create a Session object from an existing SSL_SESSION*. A reference to the + * SSL_SESSION* is stolen. + */ +ssl_SessionObj* +ssl_Session_from_SSL_SESSION(SSL_SESSION *native_session) { + ssl_SessionObj *self; + + self = PyObject_New(ssl_SessionObj, &ssl_Session_Type); + if (self == NULL) { + return NULL; + } + + return ssl_Session_init(self, native_session); +} + +/* + * Discard the reference to the OpenSSL session structure, if there is one, so + * that it can be freed if it is no longer in use. Also release the memory for + * the Python object. + */ +static void +ssl_Session_dealloc(ssl_SessionObj *self) { + if (self->session != NULL) { + SSL_SESSION_free(self->session); + self->session = NULL; + } + self->ob_type->tp_free((PyObject*)self); } /* @@ -63,16 +86,6 @@ ssl_Session_new(PyTypeObject *subtype, PyObject *args, PyObject *kwargs) { */ #define ADD_METHOD(name) { #name, (PyCFunction)ssl_Session_##name, METH_VARARGS, ssl_Session_##name##_doc } static PyMethodDef ssl_Session_methods[] = { -#if 0 - ADD_METHOD(asn1), - ADD_METHOD(get_time), - ADD_METHOD(get_timeout), -#ifdef SSL_SESSION_hash - ADD_METHOD(hash), -#endif - ADD_METHOD(set_time), - ADD_METHOD(set_timeout), -#endif { NULL, NULL } }; #undef ADD_METHOD @@ -85,7 +98,7 @@ PyTypeObject ssl_Session_Type = { "OpenSSL.SSL.Session", sizeof(ssl_SessionObj), 0, - NULL, // (destructor)ssl_Session_dealloc, /* tp_dealloc */ + (destructor)ssl_Session_dealloc, /* tp_dealloc */ NULL, /* print */ NULL, /* tp_getattr */ NULL, /* setattr */ diff --git a/OpenSSL/ssl/session.h b/OpenSSL/ssl/session.h index 98cee80..4e8de11 100644 --- a/OpenSSL/ssl/session.h +++ b/OpenSSL/ssl/session.h @@ -16,10 +16,12 @@ typedef struct { PyObject_HEAD + SSL_SESSION *session; } ssl_SessionObj; extern PyTypeObject ssl_Session_Type; extern int init_ssl_session(PyObject *); +extern ssl_SessionObj *ssl_Session_from_SSL_SESSION(SSL_SESSION *native_session); #endif diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index 529a454..b3cb1cb 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -1428,6 +1428,51 @@ class ConnectionTests(TestCase, _LoopbackMixin): self.assertIdentical(None, server.get_peer_cert_chain()) + def test_get_session_wrong_args(self): + """ + :py:obj:`Connection.get_session` raises :py:obj:`TypeError` if called + with any arguments. + """ + ctx = Context(TLSv1_METHOD) + server = Connection(ctx, None) + self.assertRaises(TypeError, server.get_session, 123) + self.assertRaises(TypeError, server.get_session, "hello") + self.assertRaises(TypeError, server.get_session, object()) + + + def test_get_session_unconnected(self): + """ + :py:obj:`Connection.get_session` returns :py:obj:`None` when used with + an object which has not been connected. + """ + ctx = Context(TLSv1_METHOD) + server = Connection(ctx, None) + session = server.get_session() + self.assertIdentical(None, session) + + + def test_server_get_session(self): + """ + On the server side of a connection, :py:obj:`Connection.get_session` + returns a :py:class:`Session` instance representing the SSL session for + that connection. + """ + server, client = self._loopback() + session = server.get_session() + self.assertTrue(session, Session) + + + def test_client_get_session(self): + """ + On the client side of a connection, :py:obj:`Connection.get_session` + returns a :py:class:`Session` instance representing the SSL session for + that connection. + """ + server, client = self._loopback() + session = client.get_session() + self.assertTrue(session, Session) + + class ConnectionGetCipherListTests(TestCase): """ -- cgit v1.2.1 From fef5c4b8a31f2ed7d641b2e75ad22e2c5920ed32 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 14 Feb 2012 16:31:52 -0500 Subject: Wrap SSL_set_session, allowing pyOpenSSL clients to actually re-use sessions now. --- OpenSSL/ssl/connection.c | 26 ++++++++++++++++ OpenSSL/test/test_ssl.py | 81 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/OpenSSL/ssl/connection.c b/OpenSSL/ssl/connection.c index 60887b0..f7994a3 100755 --- a/OpenSSL/ssl/connection.c +++ b/OpenSSL/ssl/connection.c @@ -1285,6 +1285,31 @@ ssl_Connection_get_session(ssl_ConnectionObj *self, PyObject *args) { return (PyObject*)session; } +static char ssl_Connection_set_session_doc[] = "\n\ +Set the session to be used when the TLS/SSL connection is established.\n\ +\n\ +:param session: A Session instance representing the session to use.\n\ +:returns: None\n\ +"; +static PyObject * +ssl_Connection_set_session(ssl_ConnectionObj *self, PyObject *args) { + ssl_SessionObj *session; + + if (!PyArg_ParseTuple(args, "O!:set_session", &ssl_Session_Type, &session)) { + return NULL; + } + + if (SSL_set_session(self->ssl, session->session) == 0) { + /* XXX Under what conditions does this fail? I have no idea. + */ + exception_from_error_queue(ssl_Error); + return NULL; + } + + Py_INCREF(Py_None); + return Py_None; +} + /* * Member methods in the Connection object * ADD_METHOD(name) expands to a correct PyMethodDef declaration @@ -1341,6 +1366,7 @@ static PyMethodDef ssl_Connection_methods[] = ADD_METHOD(set_accept_state), ADD_METHOD(set_connect_state), ADD_METHOD(get_session), + ADD_METHOD(set_session), { NULL, NULL } }; #undef ADD_ALIAS diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index b3cb1cb..87c8fe6 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -189,16 +189,30 @@ class _LoopbackMixin: Helper mixin which defines methods for creating a connected socket pair and for forcing two connected SSL sockets to talk to each other via memory BIOs. """ - def _loopback(self): - (server, client) = socket_pair() + def _loopbackClientFactory(self, socket): + client = Connection(Context(TLSv1_METHOD), socket) + client.set_connect_state() + return client + + def _loopbackServerFactory(self, socket): ctx = Context(TLSv1_METHOD) ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem)) - server = Connection(ctx, server) + server = Connection(ctx, socket) server.set_accept_state() - client = Connection(Context(TLSv1_METHOD), client) - client.set_connect_state() + return server + + + def _loopback(self, serverFactory=None, clientFactory=None): + if serverFactory is None: + serverFactory = self._loopbackServerFactory + if clientFactory is None: + clientFactory = self._loopbackClientFactory + + (server, client) = socket_pair() + server = serverFactory(server) + client = clientFactory(client) handshake(client, server) @@ -1473,6 +1487,63 @@ class ConnectionTests(TestCase, _LoopbackMixin): self.assertTrue(session, Session) + def test_set_session_wrong_args(self): + """ + If called with an object that is not an instance of :py:class:`Session`, + or with other than one argument, :py:obj:`Connection.set_session` raises + :py:obj:`TypeError`. + """ + ctx = Context(TLSv1_METHOD) + connection = Connection(ctx, None) + self.assertRaises(TypeError, connection.set_session) + self.assertRaises(TypeError, connection.set_session, 123) + self.assertRaises(TypeError, connection.set_session, "hello") + self.assertRaises(TypeError, connection.set_session, object()) + self.assertRaises( + TypeError, connection.set_session, Session(), Session()) + + + def test_client_set_session(self): + """ + :py:obj:`Connection.set_session`, when used prior to a connection being + established, accepts a :py:class:`Session` instance and causes an + attempt to re-use the session it represents when the SSL handshake is + performed. + """ + key = load_privatekey(FILETYPE_PEM, server_key_pem) + cert = load_certificate(FILETYPE_PEM, server_cert_pem) + ctx = Context(TLSv1_METHOD) + ctx.use_privatekey(key) + ctx.use_certificate(cert) + ctx.set_session_id("unity-test") + + def makeServer(socket): + server = Connection(ctx, socket) + server.set_accept_state() + return server + + originalServer, originalClient = self._loopback( + serverFactory=makeServer) + originalSession = originalClient.get_session() + + def makeClient(socket): + client = self._loopbackClientFactory(socket) + client.set_session(originalSession) + return client + resumedServer, resumedClient = self._loopback( + serverFactory=makeServer, + clientFactory=makeClient) + + # This is a proxy: in general, we have no access to any unique + # identifier for the session (new enough versions of OpenSSL expose a + # hash which could be usable, but "new enough" is very, very new). + # Instead, exploit the fact that the master key is re-used if the + # session is re-used. As long as the master key for the two connections + # is the same, the session was re-used! + self.assertEqual( + originalServer.master_key(), resumedServer.master_key()) + + class ConnectionGetCipherListTests(TestCase): """ -- cgit v1.2.1 From 5ea41495ab07f449339b03014c1513429b7625ac Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 14 Feb 2012 16:51:35 -0500 Subject: I don't always read OpenSSL source, but when I do I WISH I WERE DEAD --- OpenSSL/ssl/connection.c | 3 ++- OpenSSL/test/test_ssl.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/OpenSSL/ssl/connection.c b/OpenSSL/ssl/connection.c index f7994a3..ebbe39f 100755 --- a/OpenSSL/ssl/connection.c +++ b/OpenSSL/ssl/connection.c @@ -1300,7 +1300,8 @@ ssl_Connection_set_session(ssl_ConnectionObj *self, PyObject *args) { } if (SSL_set_session(self->ssl, session->session) == 0) { - /* XXX Under what conditions does this fail? I have no idea. + /* The only case which leads to this seems to be a mismatch, between + * this connection and the session, of the SSL method. */ exception_from_error_queue(ssl_Error); return NULL; diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index 87c8fe6..e241112 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -1544,6 +1544,37 @@ class ConnectionTests(TestCase, _LoopbackMixin): originalServer.master_key(), resumedServer.master_key()) + def test_set_session_wrong_method(self): + """ + """ + key = load_privatekey(FILETYPE_PEM, server_key_pem) + cert = load_certificate(FILETYPE_PEM, server_cert_pem) + ctx = Context(TLSv1_METHOD) + ctx.use_privatekey(key) + ctx.use_certificate(cert) + ctx.set_session_id("unity-test") + + def makeServer(socket): + server = Connection(ctx, socket) + server.set_accept_state() + return server + + originalServer, originalClient = self._loopback( + serverFactory=makeServer) + originalSession = originalClient.get_session() + + def makeClient(socket): + # Intentionally use a different, incompatible method here. + client = Connection(Context(SSLv3_METHOD), socket) + client.set_connect_state() + client.set_session(originalSession) + return client + + self.assertRaises( + Error, + self._loopback, clientFactory=makeClient, serverFactory=makeServer) + + class ConnectionGetCipherListTests(TestCase): """ -- cgit v1.2.1 From 068cb599bb7b3d9e05f82a7d54e6a962719e0816 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 14 Feb 2012 16:52:43 -0500 Subject: Docstrings are nice --- OpenSSL/test/test_ssl.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/OpenSSL/test/test_ssl.py b/OpenSSL/test/test_ssl.py index e241112..721c1c0 100644 --- a/OpenSSL/test/test_ssl.py +++ b/OpenSSL/test/test_ssl.py @@ -1546,6 +1546,10 @@ class ConnectionTests(TestCase, _LoopbackMixin): def test_set_session_wrong_method(self): """ + If :py:obj:`Connection.set_session` is passed a :py:class:`Session` + instance associated with a context using a different SSL method than the + :py:obj:`Connection` is using, a :py:class:`OpenSSL.SSL.Error` is + raised. """ key = load_privatekey(FILETYPE_PEM, server_key_pem) cert = load_certificate(FILETYPE_PEM, server_cert_pem) -- cgit v1.2.1 From 9c98bf96117ac820640737e15341c87fc48d34e9 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Tue, 14 Feb 2012 17:18:58 -0500 Subject: Use the future-proof-er macro to get the type structure to call the inherited free method. --- OpenSSL/ssl/session.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/OpenSSL/ssl/session.c b/OpenSSL/ssl/session.c index fb9c83f..f9932a4 100644 --- a/OpenSSL/ssl/session.c +++ b/OpenSSL/ssl/session.c @@ -73,7 +73,7 @@ ssl_Session_dealloc(ssl_SessionObj *self) { SSL_SESSION_free(self->session); self->session = NULL; } - self->ob_type->tp_free((PyObject*)self); + Py_TYPE(self)->tp_free((PyObject*)self); } /* -- cgit v1.2.1 From 6c896fea58ce9f9193f6085bab4fe7d5cf4b1360 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone Date: Thu, 16 Feb 2012 08:10:04 -0500 Subject: Add more API documentation for Session objects --- doc/api/ssl.rst | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/doc/api/ssl.rst b/doc/api/ssl.rst index 5014889..872cd5a 100644 --- a/doc/api/ssl.rst +++ b/doc/api/ssl.rst @@ -112,6 +112,15 @@ Context, Connection. :py:const:`SSLv23_METHOD` or :py:const:`TLSv1_METHOD`. +.. py:class:: Session() + + A class representing an SSL session. A session defines certain connection + parameters which may be re-used to speed up the setup of subsequent + connections. + + .. versionadded:: 0.14 + + .. py:data:: ConnectionType See :py:class:`Connection`. @@ -427,6 +436,14 @@ Context objects have the following methods: .. versionadded:: 0.13 +.. _openssl-session: + +Session objects +--------------- + +Session objects have no methods. + + .. _openssl-connection: Connection objects @@ -700,6 +717,22 @@ Connection objects have the following methods: .. versionadded:: 0.13 +.. py:method:: Connection.get_session() + + Get a :py:class:`Session` instance representing the SSL session in use by + the connection, or :py:obj:`None` if there is no session. + + .. versionadded:: 0.14 + + +.. py:method:: Connection.set_session(session) + + Set a new SSL session (using a :py:class:`Session` instance) to be used by + the connection. + + .. versionadded:: 0.14 + + .. Rubric:: Footnotes .. [#connection-context-socket] Actually, all that is required is an object that -- cgit v1.2.1