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]

Reply via email to