Author: kgiusti
Date: Mon Dec 10 19:14:00 2012
New Revision: 1419677
URL: http://svn.apache.org/viewvc?rev=1419677&view=rev
Log:
PROTON-136: refactor session resume and corresponding test
Modified:
qpid/proton/branches/kgiusti-proton-136/proton-c/bindings/python/proton.py
qpid/proton/branches/kgiusti-proton-136/proton-c/include/proton/ssl.h
qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/openssl.c
qpid/proton/branches/kgiusti-proton-136/tests/proton_tests/ssl.py
Modified:
qpid/proton/branches/kgiusti-proton-136/proton-c/bindings/python/proton.py
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/kgiusti-proton-136/proton-c/bindings/python/proton.py?rev=1419677&r1=1419676&r2=1419677&view=diff
==============================================================================
--- qpid/proton/branches/kgiusti-proton-136/proton-c/bindings/python/proton.py
(original)
+++ qpid/proton/branches/kgiusti-proton-136/proton-c/bindings/python/proton.py
Mon Dec 10 19:14:00 2012
@@ -2277,9 +2277,11 @@ class SSL(object):
else:
return err
- def __init__(self, transport, domain=None):
+ def __init__(self, transport, domain=None, session_id=None):
if domain:
- self._ssl = pn_ssl_new( domain._domain, transport._trans )
+ if session_id:
+ session_id = str(session_id)
+ self._ssl = pn_ssl_new( domain._domain, transport._trans, session_id )
else: # old api:
self._ssl = pn_ssl(transport._trans)
if self._ssl is None:
@@ -2324,32 +2326,18 @@ class SSL(object):
return name
return None
- def get_state(self):
- return SSLState( self )
+ RESUME_UNKNOWN = PN_SSL_RESUME_UNKNOWN
+ RESUME_NEW = PN_SSL_RESUME_NEW
+ RESUME_REUSED = PN_SSL_RESUME_REUSED
- def resume_state(self, state):
- return pn_ssl_resume_state( self._ssl, state._state )
-
- def state_resumed_ok(self):
- return pn_ssl_state_resumed_ok( self._ssl )
-
-class SSLState(object):
- """ State to store an SSL session. Used to resume previous session on a new
- SSL connection.
- """
- def __init__(self, ssl_obj):
- self._state = pn_ssl_get_state( ssl_obj._ssl )
-
- def __del__(self):
- if hasattr(self, "_state"):
- pn_ssl_state_free( self._state )
- del self._state
+ def resume_status(self):
+ return pn_ssl_resume_status( self._ssl )
__all__ = ["Messenger", "Message", "ProtonException", "MessengerException",
"MessageException", "Timeout", "Data", "Endpoint", "Connection",
"Session", "Link", "Terminus", "Sender", "Receiver", "Delivery",
"Transport", "TransportException", "SASL", "UNDESCRIBED", "SSL",
- "SSLDomain", "SSLState", "Described", "Array", "symbol", "char",
+ "SSLDomain", "Described", "Array", "symbol", "char",
"timestamp", "ulong", "SSLUnavailable", "PN_SESSION_WINDOW",
"AUTOMATIC", "MANUAL", "PENDING", "ACCEPTED", "REJECTED"]
Modified: qpid/proton/branches/kgiusti-proton-136/proton-c/include/proton/ssl.h
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/kgiusti-proton-136/proton-c/include/proton/ssl.h?rev=1419677&r1=1419676&r2=1419677&view=diff
==============================================================================
--- qpid/proton/branches/kgiusti-proton-136/proton-c/include/proton/ssl.h
(original)
+++ qpid/proton/branches/kgiusti-proton-136/proton-c/include/proton/ssl.h Mon
Dec 10 19:14:00 2012
@@ -75,7 +75,6 @@ extern "C" {
typedef struct pn_ssl_domain_t pn_ssl_domain_t;
typedef struct pn_ssl_t pn_ssl_t;
-typedef struct pn_ssl_state_t pn_ssl_state_t;
/** Determines the type of SSL endpoint. */
typedef enum {
@@ -200,7 +199,9 @@ int pn_ssl_domain_set_default_peer_authe
* @return a pointer to the SSL object configured for this transport. Returns
NULL if SSL
* cannot be provided, which would occur if no SSL support is available.
*/
-pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *domain, pn_transport_t *transport);
+pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *domain,
+ pn_transport_t *transport,
+ const char *session_id);
/** Permit a server to accept connection requests from non-SSL clients.
*
@@ -274,40 +275,6 @@ bool pn_ssl_get_cipher_name(pn_ssl_t *ss
*/
bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *buffer, size_t size);
-/** Obtain a handle to the SSL parameters negotiated for this SSL session.
- *
- * Used for client session resume. Returns a handle to the negotiated SSL
parameters
- * that may be restored on a later connection attempt to the same peer. See
- * ::pn_ssl_resume_state. Must be released by calling :pn_ssl_state_free when
done.
- *
- * @note The state is only valid once an SSL session has been sucessfully
established. It
- * is therefore recommended to retrieve the state after the transport has
successfully
- * exchanged data with the peer, prior to shutting down the connection.
- *
- * @param[in] ssl the session whose negotiated parameters are to be restored
at a later date.
- * @return a pointer to the negotated state, or NULL if session resume is not
supported.
- */
-pn_ssl_state_t *pn_ssl_get_state( pn_ssl_t *ssl );
-
-/** Resume previously negotiated parameters on a new session.
- *
- * Used for client session resume. State can only be resumed on new
connections to the
- * same peer as the original session and using the same configuration
parameters. See
- * ::pn_ssl_get_state.
- *
- * @note This method must be called prior to allowing traffic over the
connection.
- *
- * @note This is a best-effort service - there is no guarantee that the remote
server will
- * accept the resumed parameters. The remote server may choose to ignore these
- * parameters, and request a re-negotiation instead.
- *
- * @param[in] ssl the session for with the parameters should be used.
- * @param[in] state the previously negotiated parameters to use.
- * @return 0 if the ssl session will accept the state parameter. This does
not guarantee
- * that the remote will allow the state to be resumed (see
::pn_ssl_state_resumed_ok).
- */
-int pn_ssl_resume_state( pn_ssl_t *ssl, pn_ssl_state_t *state );
-
/** Check whether the state has been resumed.
*
* Used for client session resume. When called on an active session,
indicates whether
@@ -321,19 +288,12 @@ int pn_ssl_resume_state( pn_ssl_t *ssl,
* @return true if the ssl session was resumed, false if the Server required a
* re-negotiation instead.
*/
-bool pn_ssl_state_resumed_ok( pn_ssl_t *ssl );
-
-/** Release a state handle previously obtained via ::pn_ssl_get_state.
- *
- * Used for client session resume. Once obtained, the state remains valid
until it is
- * freed using this method. This allows the state to remain valid after the
original SSL
- * session-related objects (pn_ssl_t and/or pn_ssl_domain_t) have been
closed/deallocated.
- *
- * @param[in] state the state object that will be released.
- */
-void pn_ssl_state_free( pn_ssl_state_t *state );
-
-
+typedef enum {
+ PN_SSL_RESUME_UNKNOWN,
+ PN_SSL_RESUME_NEW,
+ PN_SSL_RESUME_REUSED
+} pn_ssl_resume_status_t;
+pn_ssl_resume_status_t pn_ssl_resume_status( pn_ssl_t *ssl );
/** original API: */
Modified: qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/openssl.c
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/openssl.c?rev=1419677&r1=1419676&r2=1419677&view=diff
==============================================================================
--- qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/openssl.c
(original)
+++ qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/openssl.c Mon Dec
10 19:14:00 2012
@@ -23,6 +23,7 @@
#include "./ssl-internal.h"
#include <proton/engine.h>
#include "../engine/engine-internal.h"
+#include "../platform.h"
#include "../util.h"
#include <openssl/ssl.h>
@@ -43,6 +44,7 @@
static int ssl_initialized;
typedef enum { UNKNOWN_CONNECTION, SSL_CONNECTION, CLEAR_CONNECTION }
connection_mode_t;
+typedef struct pn_ssl_session_t pn_ssl_session_t;
struct pn_ssl_domain_t {
int ref_count;
@@ -57,6 +59,10 @@ struct pn_ssl_domain_t {
// settings used for all connections
char *default_trusted_CAs;
pn_ssl_verify_mode_t default_verify_mode;
+
+ // session cache
+ pn_ssl_session_t *ssn_cache_head;
+ pn_ssl_session_t *ssn_cache_tail;
};
@@ -65,11 +71,11 @@ struct pn_ssl_t {
pn_ssl_domain_t *domain;
bool private_domain; // domain used exclusive to this SSL
SSL *ssl;
- SSL_SESSION *session;
bool allow_unsecured; // allow non-SSL connections
pn_ssl_verify_mode_t verify_mode; // can be overridden
- char *trusted_CAs; // for this connection
+ const char *trusted_CAs; // for this connection
+ const char *session_id;
pn_transport_t *transport;
@@ -98,6 +104,14 @@ struct pn_ssl_t {
pn_trace_t trace;
};
+struct pn_ssl_session_t {
+ const char *id;
+ SSL_SESSION *session;
+ pn_ssl_session_t *ssn_cache_next;
+ pn_ssl_session_t *ssn_cache_prev;
+};
+
+
// define two sets of allowable ciphers: those that require authentication,
and those
// that do not require authentication (anonymous). See ciphers(1).
#define CIPHERS_AUTHENTICATE "ALL:!aNULL:!eNULL:@STRENGTH"
@@ -114,6 +128,8 @@ static ssize_t process_output_unknown(pn
static connection_mode_t check_for_ssl_connection( const char *data, size_t
len );
static int init_ssl_socket( pn_ssl_t * );
static void release_ssl_socket( pn_ssl_t * );
+static pn_ssl_session_t *ssn_cache_find( pn_ssl_domain_t *, const char * );
+static void ssl_session_free( pn_ssl_session_t *);
// @todo: used to avoid littering the code with calls to printf...
@@ -272,6 +288,39 @@ static DH *get_dh2048()
return(dh);
}
+static pn_ssl_session_t *ssn_cache_find( pn_ssl_domain_t *domain, const char
*id )
+{
+ pn_timestamp_t now_msec = pn_i_now();
+ long now_sec = (long)(now_msec / 1000);
+ pn_ssl_session_t *ssn = LL_HEAD( domain, ssn_cache );
+ while (ssn) {
+ long expire = SSL_SESSION_get_time( ssn->session )
+ + SSL_SESSION_get_timeout( ssn->session );
+ if (expire < now_sec) {
+ pn_ssl_session_t *next = ssn->ssn_cache_next;
+ LL_REMOVE( domain, ssn_cache, ssn );
+ ssl_session_free( ssn );
+ ssn = next;
+ continue;
+ }
+
+ if (!strcmp(ssn->id, id)) {
+ break;
+ }
+ ssn = ssn->ssn_cache_next;
+ }
+ return ssn;
+}
+
+static void ssl_session_free( pn_ssl_session_t *ssn)
+{
+ if (ssn) {
+ if (ssn->id) free( (void *)ssn->id );
+ if (ssn->session) SSL_SESSION_free( ssn->session );
+ free( ssn );
+ }
+}
+
/** Public API - visible to application code */
@@ -341,6 +390,15 @@ pn_ssl_domain_t *pn_ssl_domain( pn_ssl_m
void pn_ssl_domain_free( pn_ssl_domain_t *domain )
{
if (--domain->ref_count == 0) {
+
+ pn_ssl_session_t *ssn = LL_HEAD( domain, ssn_cache );
+ while (ssn) {
+ pn_ssl_session_t *next = ssn->ssn_cache_next;
+ LL_REMOVE( domain, ssn_cache, ssn );
+ ssl_session_free( ssn );
+ ssn = next;
+ }
+
if (domain->ctx) SSL_CTX_free(domain->ctx);
if (domain->keyfile_pw) free(domain->keyfile_pw);
if (domain->default_trusted_CAs) free(domain->default_trusted_CAs);
@@ -486,7 +544,7 @@ int pn_ssl_domain_set_default_peer_authe
}
-pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *domain, pn_transport_t *transport)
+pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *domain, pn_transport_t *transport,
const char *session_id)
{
if (!transport || !domain) return NULL;
if (transport->ssl) return transport->ssl;
@@ -496,6 +554,8 @@ pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *d
ssl->domain = domain;
domain->ref_count++;
+ if (session_id && domain->mode == PN_SSL_MODE_CLIENT)
+ ssl->session_id = pn_strdup(session_id);
if (init_ssl_socket(ssl)) {
pn_ssl_free(ssl);
@@ -563,7 +623,7 @@ int pn_ssl_set_peer_authentication(pn_ss
" Use pn_ssl_domain_set_credentials()\n");
}
- if (ssl->trusted_CAs) free(ssl->trusted_CAs);
+ if (ssl->trusted_CAs) free( (void *)ssl->trusted_CAs);
ssl->trusted_CAs = pn_strdup( trusted_CAs );
STACK_OF(X509_NAME) *cert_names;
cert_names = SSL_load_client_CA_file( ssl->trusted_CAs );
@@ -605,7 +665,7 @@ int pn_ssl_get_peer_authentication(pn_ss
if (!ssl) return -1;
pn_ssl_verify_mode_t my_mode = ssl->verify_mode;
- char *my_trusted_CAs = ssl->trusted_CAs;
+ const char *my_trusted_CAs = ssl->trusted_CAs;
if (ssl->verify_mode == PN_SSL_VERIFY_NULL && ssl->domain) {
// using the parent domain's values:
@@ -666,7 +726,8 @@ void pn_ssl_free( pn_ssl_t *ssl)
_log( ssl, "SSL socket freed.\n" );
release_ssl_socket( ssl );
if (ssl->domain) pn_ssl_domain_free(ssl->domain);
- if (ssl->trusted_CAs) free(ssl->trusted_CAs);
+ if (ssl->trusted_CAs) free((void *)ssl->trusted_CAs);
+ if (ssl->session_id) free((void *)ssl->session_id);
free(ssl);
}
@@ -814,6 +875,20 @@ static int start_ssl_shutdown( pn_ssl_t
{
if (!ssl->ssl_shutdown) {
_log(ssl, "Shutting down SSL connection...\n");
+ if (ssl->session_id) {
+ // save the negotiated credentials before we close the connection
+ pn_ssl_session_t *ssn = (pn_ssl_session_t *)calloc( 1,
sizeof(pn_ssl_session_t));
+ if (ssn) {
+ ssn->id = pn_strdup( ssl->session_id );
+ ssn->session = SSL_get1_session( ssl->ssl );
+ if (ssn->session) {
+ _log( ssl, "Saving SSL session as %s\n", ssl->session_id );
+ LL_ADD( ssl->domain, ssn_cache, ssn );
+ } else {
+ ssl_session_free( ssn );
+ }
+ }
+ }
ssl->ssl_shutdown = true;
BIO_ssl_shutdown( ssl->bio_ssl );
}
@@ -1077,6 +1152,20 @@ static int init_ssl_socket( pn_ssl_t *ss
return -1;
}
+ // restore session, if available
+ if (ssl->session_id) {
+ pn_ssl_session_t *ssn = ssn_cache_find( ssl->domain, ssl->session_id );
+ if (ssn) {
+ _log( ssl, "Restoring previous session id=%s\n", ssn->id );
+ int rc = SSL_set_session( ssl->ssl, ssn->session );
+ if (rc != 1) {
+ _log( ssl, "Session restore failed, id=%s\n", ssn->id );
+ }
+ LL_REMOVE( ssl->domain, ssn_cache, ssn );
+ ssl_session_free( ssn );
+ }
+ }
+
// now layer a BIO over the SSL socket
ssl->bio_ssl = BIO_new(BIO_f_ssl());
if (!ssl->bio_ssl) {
@@ -1224,30 +1313,14 @@ void pn_ssl_trace(pn_ssl_t *ssl, pn_trac
ssl->trace = trace;
}
-
-/* the new stuff */
-
-pn_ssl_state_t *pn_ssl_get_state( pn_ssl_t *ssl)
-{
- if (!ssl || !ssl->ssl) return NULL;
- return (pn_ssl_state_t *)SSL_get1_session(ssl->ssl);
-}
-
-int pn_ssl_resume_state( pn_ssl_t *ssl, pn_ssl_state_t *state )
+pn_ssl_resume_status_t pn_ssl_resume_status( pn_ssl_t *ssl )
{
- if (!ssl || !ssl->ssl) return -1;
- int rc = SSL_set_session( ssl->ssl, (SSL_SESSION *)state);
- if (rc == 1) return 0;
- return -1;
-}
-
-bool pn_ssl_state_resumed_ok( pn_ssl_t *ssl )
-{
- if (!ssl || !ssl->ssl) return false;
- return SSL_session_reused( ssl->ssl ) == 1;
+ if (!ssl || !ssl->ssl) return PN_SSL_RESUME_UNKNOWN;
+ switch (SSL_session_reused( ssl->ssl )) {
+ case 0: return PN_SSL_RESUME_NEW;
+ case 1: return PN_SSL_RESUME_REUSED;
+ default: break;
+ }
+ return PN_SSL_RESUME_UNKNOWN;
}
-void pn_ssl_state_free( pn_ssl_state_t *state )
-{
- SSL_SESSION_free( (SSL_SESSION *)state );
-}
Modified: qpid/proton/branches/kgiusti-proton-136/tests/proton_tests/ssl.py
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/kgiusti-proton-136/tests/proton_tests/ssl.py?rev=1419677&r1=1419676&r2=1419677&view=diff
==============================================================================
--- qpid/proton/branches/kgiusti-proton-136/tests/proton_tests/ssl.py (original)
+++ qpid/proton/branches/kgiusti-proton-136/tests/proton_tests/ssl.py Mon Dec
10 19:14:00 2012
@@ -42,7 +42,7 @@ class SslTest(common.Test):
class SslTestConnection(object):
""" Represents a single SSL connection.
"""
- def __init__(self, domain=None):
+ def __init__(self, domain=None, session_id=None):
try:
self.ssl = None
self.domain = domain
@@ -50,7 +50,7 @@ class SslTest(common.Test):
self.connection = Connection()
self.transport.bind(self.connection)
if domain:
- self.ssl = SSL( self.transport, self.domain )
+ self.ssl = SSL( self.transport, self.domain, session_id )
except SSLUnavailable, e:
raise Skipped(e)
@@ -365,35 +365,26 @@ class SslTest(common.Test):
self.client_domain.set_default_peer_authentication( SSL.VERIFY_PEER )
server = SslTest.SslTestConnection( self.server_domain )
- client = SslTest.SslTestConnection( self.client_domain )
+ client = SslTest.SslTestConnection( self.client_domain,
"my-session-id" )
# bring up the connection and store its state
client.connection.open()
server.connection.open()
self._pump( client, server )
assert client.ssl.protocol_name() is not None
- ssl_state = client.ssl.get_state()
# cleanly shutdown the connection
client.connection.close()
server.connection.close()
self._pump( client, server )
- # destroy the existing clients, and client domain
+ # destroy the existing clients
del client
del server
- del self.client_domain
-
- # now create a new set of connections
- self.client_domain = SSLDomain(SSL.MODE_CLIENT)
-
self.client_domain.set_trusted_ca_db(self._testpath("ca-certificate.pem"))
- self.client_domain.set_default_peer_authentication( SSL.VERIFY_PEER )
+ # now create a new set of connections, use last session id
server = SslTest.SslTestConnection( self.server_domain )
- client = SslTest.SslTestConnection( self.client_domain )
-
- # resume the client state before attempting to connect
- client.ssl.resume_state( ssl_state )
+ client = SslTest.SslTestConnection( self.client_domain,
"my-session-id" )
#client.transport.trace(Transport.TRACE_DRV)
#server.transport.trace(Transport.TRACE_DRV)
@@ -402,7 +393,25 @@ class SslTest(common.Test):
server.connection.open()
self._pump( client, server )
assert server.ssl.protocol_name() is not None
- assert client.ssl.state_resumed_ok()
+ assert client.ssl.resume_status() == SSL.RESUME_REUSED
+ client.connection.close()
+ server.connection.close()
+ self._pump( client, server )
+
+ # now try to resume using an unknown session-id, expect resume to fail
+ # and a new session is negotiated
+
+ del client
+ del server
+
+ server = SslTest.SslTestConnection( self.server_domain )
+ client = SslTest.SslTestConnection( self.client_domain,
"some-other-session-id" )
+
+ client.connection.open()
+ server.connection.open()
+ self._pump( client, server )
+ assert server.ssl.protocol_name() is not None
+ assert client.ssl.resume_status() == SSL.RESUME_NEW
client.connection.close()
server.connection.close()
self._pump( client, server )
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]