You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by kg...@apache.org on 2012/12/04 19:17:35 UTC

svn commit: r1417082 - in /qpid/proton/branches/kgiusti-proton-136: proton-c/bindings/python/proton.py proton-c/include/proton/ssl.h proton-c/src/ssl/openssl.c proton-c/src/ssl/ssl_stub.c tests/proton_tests/ssl.py

Author: kgiusti
Date: Tue Dec  4 18:17:34 2012
New Revision: 1417082

URL: http://svn.apache.org/viewvc?rev=1417082&view=rev
Log:
Pull in SSL API rework from private branch.

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/proton-c/src/ssl/ssl_stub.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=1417082&r1=1417081&r2=1417082&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 Tue Dec  4 18:17:34 2012
@@ -2243,6 +2243,31 @@ class SSLException(TransportException):
 class SSLUnavailable(SSLException):
   pass
 
+class SSLDomain(object):
+  def __init__(self, mode):
+    self._domain = pn_ssl_domain(mode)
+    if self._domain is None:
+      raise SSLUnavailable()
+
+  def _check(self, err):
+    if err < 0:
+      exc = EXCEPTIONS.get(err, SSLException)
+      raise exc("SSL failure.")
+    else:
+      return err
+
+  def set_credentials(self, cert_file, key_file, password):
+    return self._check( pn_ssl_domain_set_credentials(self._domain,
+                                                      cert_file, key_file,
+                                                      password) )
+  def set_trusted_ca_db(self, certificate_db):
+    return self._check( pn_ssl_domain_set_trusted_ca_db(self._domain,
+                                                        certificate_db) )
+  def set_default_peer_authentication(self, verify_mode, trusted_CAs=None):
+    return self._check( pn_ssl_domain_set_default_peer_authentication(self._domain,
+                                                                      verify_mode,
+                                                                      trusted_CAs) )
+
 class SSL(object):
 
   def _check(self, err):
@@ -2252,8 +2277,11 @@ class SSL(object):
     else:
       return err
 
-  def __init__(self, transport):
-    self._ssl = pn_ssl(transport._trans)
+  def __init__(self, transport, domain=None):
+    if domain:
+      self._ssl = pn_ssl_new( domain._domain, transport._trans )
+    else:   # old api:
+      self._ssl = pn_ssl(transport._trans)
     if self._ssl is None:
       raise SSLUnavailable()
 
@@ -2296,10 +2324,32 @@ class SSL(object):
       return name
     return None
 
+  def get_state(self):
+    return SSLState( self )
+
+  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
+
+
 __all__ = ["Messenger", "Message", "ProtonException", "MessengerException",
            "MessageException", "Timeout", "Data", "Endpoint", "Connection",
            "Session", "Link", "Terminus", "Sender", "Receiver", "Delivery",
-           "Transport", "TransportException", "SASL", "SSL", "Described",
-           "Array", "symbol", "char", "timestamp", "ulong", "UNDESCRIBED",
-           "SSLUnavailable", "PN_SESSION_WINDOW", "AUTOMATIC", "MANUAL",
-           "PENDING", "ACCEPTED", "REJECTED"]
+           "Transport", "TransportException", "SASL", "UNDESCRIBED", "SSL",
+           "SSLDomain", "SSLState", "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=1417082&r1=1417081&r2=1417082&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 Tue Dec  4 18:17:34 2012
@@ -140,6 +140,7 @@ int pn_ssl_allow_unsecured_client(pn_ssl
  *  These settings can be changed via ::pn_ssl_set_peer_authentication()
  */
 typedef enum {
+  PN_SSL_VERIFY_NULL=0,   /**< internal use only */
   PN_SSL_VERIFY_PEER,     /**< require peer to provide a valid identifying certificate */
   PN_SSL_ANONYMOUS_PEER,  /**< do not require a certificate nor cipher authorization */
 } pn_ssl_verify_mode_t;
@@ -210,6 +211,36 @@ bool pn_ssl_get_cipher_name(pn_ssl_t *ss
  * @return True if the version information was to buffer, False if SSL connection not ready.
  */
 bool pn_ssl_get_protocol_name(pn_ssl_t *ssl, char *buffer, size_t size);
+
+
+  /* the new stuff */
+typedef struct pn_ssl_domain_t pn_ssl_domain_t;
+typedef struct pn_ssl_state_t pn_ssl_state_t;
+
+pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t );
+void pn_ssl_domain_free( pn_ssl_domain_t * );
+
+pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *, pn_transport_t *);
+
+int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
+                               const char *certificate_file,
+                               const char *private_key_file,
+                               const char *password);
+
+int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t *domain,
+                                const char *certificate_db);
+
+int pn_ssl_domain_set_default_peer_authentication(pn_ssl_domain_t *domain,
+                                                  const pn_ssl_verify_mode_t mode,
+                                                  const char *trusted_CAs);
+
+
+pn_ssl_state_t *pn_ssl_get_state( pn_ssl_t *);
+int pn_ssl_resume_state( pn_ssl_t *, pn_ssl_state_t * );
+bool pn_ssl_state_resumed_ok( pn_ssl_t * );
+void pn_ssl_state_free( pn_ssl_state_t * );
+  
+
 #ifdef __cplusplus
 }
 #endif

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=1417082&r1=1417081&r2=1417082&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 Tue Dec  4 18:17:34 2012
@@ -44,16 +44,32 @@ static int ssl_initialized;
 
 typedef enum { UNKNOWN_CONNECTION, SSL_CONNECTION, CLEAR_CONNECTION } connection_mode_t;
 
-struct pn_ssl_t {
-  SSL_CTX *ctx;
-  SSL *ssl;
+struct pn_ssl_domain_t {
+  int   ref_count;
+
+  SSL_CTX       *ctx;
   pn_ssl_mode_t mode;
-  bool allow_unsecured; // allow non-SSL connections
+
   bool has_ca_db;       // true when CA database configured
   bool has_certificate; // true when certificate configured
   char *keyfile_pw;
-  pn_ssl_verify_mode_t verify_mode;
-  char *trusted_CAs;
+
+  // settings used for all connections
+  char *default_trusted_CAs;
+  pn_ssl_verify_mode_t default_verify_mode;
+};
+
+
+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
 
   pn_transport_t *transport;
 
@@ -97,6 +113,7 @@ static ssize_t process_input_unknown(pn_
 static ssize_t process_output_unknown(pn_transport_t *transport, char *input_data, size_t len);
 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 * );
 
 
 // @todo: used to avoid littering the code with calls to printf...
@@ -120,7 +137,7 @@ static void _log(pn_ssl_t *ssl, const ch
 }
 
 // log an error and dump the SSL error stack
-static void _log_ssl_error(pn_ssl_t *ssl, const char *fmt, ...)
+static void _log_ssl_error( const char *fmt, ...)
 {
   char buf[128];        // see "man ERR_error_string_n()"
   va_list ap;
@@ -159,7 +176,7 @@ static int ssl_failed(pn_ssl_t *ssl)
   if (ssl_err) {
     ERR_error_string_n( ssl_err, buf, sizeof(buf) );
   }
-  _log_ssl_error(ssl, NULL);    // spit out any remaining errors to the log file
+  _log_ssl_error(NULL);    // spit out any remaining errors to the log file
   return pn_error_format( ssl->transport->error, PN_ERR, "SSL Failure: %s", buf );
 }
 
@@ -258,63 +275,127 @@ static DH *get_dh2048()
 
 /** Public API - visible to application code */
 
+pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode )
+{
+  if (!ssl_initialized) {
+    ssl_initialized = 1;
+    SSL_library_init();
+    SSL_load_error_strings();
+    OpenSSL_add_all_algorithms();
+  }
+
+  pn_ssl_domain_t *domain = calloc(1, sizeof(pn_ssl_domain_t));
+  if (!domain) return NULL;
 
-int pn_ssl_set_credentials( pn_ssl_t *ssl,
-                            const char *certificate_file,
-                            const char *private_key_file,
-                            const char *password)
+  domain->ref_count = 1;
+  domain->mode = mode;
+  switch(mode) {
+  case PN_SSL_MODE_CLIENT:
+    domain->ctx = SSL_CTX_new(TLSv1_client_method());
+    if (!domain->ctx) {
+      _log_ssl_error( "Unable to initialize OpenSSL context.\n");
+      free(domain);
+      return NULL;
+    }
+    break;
+
+  case PN_SSL_MODE_SERVER:
+    domain->ctx = SSL_CTX_new(SSLv23_server_method());
+    if (!domain->ctx) {
+      _log_ssl_error("Unable to initialize OpenSSL context.\n");
+      free(domain);
+      return NULL;
+    }
+    SSL_CTX_set_options(domain->ctx, SSL_OP_NO_SSLv2);  // v2 is insecure
+    break;
+
+  default:
+    _log_error("Invalid valid for pn_ssl_mode_t: %d\n", mode);
+    free(domain);
+    return NULL;
+  }
+
+  // by default, allow anonymous ciphers so certificates are not required 'out of the box'
+  if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_ANONYMOUS )) {
+    _log_ssl_error("Failed to set cipher list to %s\n", CIPHERS_ANONYMOUS);
+    pn_ssl_domain_free(domain);
+    return NULL;
+  }
+
+  // ditto: by default do not authenticate the peer (can be done by SASL).
+  if (pn_ssl_domain_set_default_peer_authentication( domain, PN_SSL_ANONYMOUS_PEER, NULL )) {
+    pn_ssl_domain_free(domain);
+    return NULL;
+  }
+
+  DH *dh = get_dh2048();
+  if (dh) {
+    SSL_CTX_set_tmp_dh(domain->ctx, dh);
+    DH_free(dh);
+    SSL_CTX_set_options(domain->ctx, SSL_OP_SINGLE_DH_USE);
+  }
+
+  return domain;
+}
+
+void pn_ssl_domain_free( pn_ssl_domain_t *domain )
 {
-  if (!ssl) return -1;
-  if (ssl->ssl) {
-    _log_error("Error: attempting to set credentials while SSL in use.\n");
-    return -1;
+  if (--domain->ref_count == 0) {
+    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);
+    free(domain);
   }
+}
+
 
-  if (SSL_CTX_use_certificate_chain_file(ssl->ctx, certificate_file) != 1) {
-    _log_ssl_error(ssl, "SSL_CTX_use_certificate_chain_file( %s ) failed\n", certificate_file);
+int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
+                               const char *certificate_file,
+                               const char *private_key_file,
+                               const char *password)
+{
+  if (!domain || !domain->ctx) return -1;
+
+  if (SSL_CTX_use_certificate_chain_file(domain->ctx, certificate_file) != 1) {
+    _log_ssl_error( "SSL_CTX_use_certificate_chain_file( %s ) failed\n", certificate_file);
     return -3;
   }
 
   if (password) {
-    ssl->keyfile_pw = pn_strdup(password);  // @todo: obfuscate me!!!
-    SSL_CTX_set_default_passwd_cb(ssl->ctx, keyfile_pw_cb);
-    SSL_CTX_set_default_passwd_cb_userdata(ssl->ctx, ssl->keyfile_pw);
+    domain->keyfile_pw = pn_strdup(password);  // @todo: obfuscate me!!!
+    SSL_CTX_set_default_passwd_cb(domain->ctx, keyfile_pw_cb);
+    SSL_CTX_set_default_passwd_cb_userdata(domain->ctx, domain->keyfile_pw);
   }
 
-  if (SSL_CTX_use_PrivateKey_file(ssl->ctx, private_key_file, SSL_FILETYPE_PEM) != 1) {
-    _log_ssl_error(ssl, "SSL_CTX_use_PrivateKey_file( %s ) failed\n", private_key_file);
+  if (SSL_CTX_use_PrivateKey_file(domain->ctx, private_key_file, SSL_FILETYPE_PEM) != 1) {
+    _log_ssl_error( "SSL_CTX_use_PrivateKey_file( %s ) failed\n", private_key_file);
     return -4;
   }
 
-  if (SSL_CTX_check_private_key(ssl->ctx) != 1) {
-    _log_ssl_error(ssl, "The key file %s is not consistent with the certificate %s\n",
+  if (SSL_CTX_check_private_key(domain->ctx) != 1) {
+    _log_ssl_error( "The key file %s is not consistent with the certificate %s\n",
                    private_key_file, certificate_file);
     return -5;
   }
 
-  ssl->has_certificate = true;
+  domain->has_certificate = true;
 
   // bug in older versions of OpenSSL: servers may request client cert even if anonymous
   // cipher was negotiated.  TLSv1 will reject such a request.  Hack: once a cert is
   // configured, allow only authenticated ciphers.
-  if (!SSL_CTX_set_cipher_list( ssl->ctx, CIPHERS_AUTHENTICATE )) {
-      _log_ssl_error(ssl, "Failed to set cipher list to %s\n", CIPHERS_AUTHENTICATE);
+  if (!SSL_CTX_set_cipher_list( domain->ctx, CIPHERS_AUTHENTICATE )) {
+      _log_ssl_error( "Failed to set cipher list to %s\n", CIPHERS_AUTHENTICATE);
       return -6;
   }
 
-  _log( ssl, "Configured local certificate file %s\n", certificate_file );
   return 0;
 }
 
 
-int pn_ssl_set_trusted_ca_db(pn_ssl_t *ssl,
-                             const char *certificate_db)
+int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t *domain,
+                                    const char *certificate_db)
 {
-  if (!ssl) return 0;
-  if (ssl->ssl) {
-    _log_error("Error: attempting to set trusted CA db after SSL connection initialized.\n");
-    return -1;
-  }
+  if (!domain) return -1;
 
   // certificates can be either a file or a directory, which determines how it is passed
   // to SSL_CTX_load_verify_locations()
@@ -334,22 +415,108 @@ int pn_ssl_set_trusted_ca_db(pn_ssl_t *s
     file = certificate_db;
   }
 
-  if (SSL_CTX_load_verify_locations( ssl->ctx, file, dir ) != 1) {
-    _log_ssl_error(ssl, "SSL_CTX_load_verify_locations( %s ) failed\n", certificate_db);
+  if (SSL_CTX_load_verify_locations( domain->ctx, file, dir ) != 1) {
+    _log_ssl_error( "SSL_CTX_load_verify_locations( %s ) failed\n", certificate_db);
     return -1;
   }
 
-  ssl->has_ca_db = true;
+  domain->has_ca_db = true;
 
-  _log( ssl, "loaded trusted CA database: file=%s dir=%s\n", file, dir );
   return 0;
 }
 
 
+int pn_ssl_domain_set_default_peer_authentication(pn_ssl_domain_t *domain,
+                                                  const pn_ssl_verify_mode_t mode,
+                                                  const char *trusted_CAs)
+{
+  if (!domain) return -1;
+
+  switch (mode) {
+  case PN_SSL_VERIFY_PEER:
+
+    if (!domain->has_ca_db) {
+      _log_error("Error: cannot verify peer without a trusted CA configured.\n"
+                 "       Use pn_ssl_domain_set_trusted_ca_db()\n");
+      return -1;
+    }
+
+    if (domain->mode == PN_SSL_MODE_SERVER) {
+      // openssl requires that server connections supply a list of trusted CAs which is
+      // sent to the client
+      if (!trusted_CAs) {
+        _log_error("Error: a list of trusted CAs must be provided.\n");
+        return -1;
+      }
+      if (!domain->has_certificate) {
+      _log_error("Error: Server cannot verify peer without configuring a certificate.\n"
+                 "       Use pn_ssl_domain_set_credentials()\n");
+      }
+
+      if (domain->default_trusted_CAs) free(domain->default_trusted_CAs);
+      domain->default_trusted_CAs = pn_strdup( trusted_CAs );
+      STACK_OF(X509_NAME) *cert_names;
+      cert_names = SSL_load_client_CA_file( domain->default_trusted_CAs );
+      if (cert_names != NULL)
+        SSL_CTX_set_client_CA_list(domain->ctx, cert_names);
+      else {
+        _log_error("Error: Unable to process file of trusted CAs: %s\n", trusted_CAs);
+        return -1;
+      }
+    }
+
+    SSL_CTX_set_verify( domain->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+    // verify_callback /*?verify callback?*/ );
+#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
+    SSL_CTX_set_verify_depth(domain->ctx, 1);
+#endif
+    break;
+
+  case PN_SSL_ANONYMOUS_PEER:   // hippie free love mode... :)
+    SSL_CTX_set_verify( domain->ctx, SSL_VERIFY_NONE, NULL );
+    break;
+
+  default:
+    _log_error( "Invalid peer authentication mode given.\n" );
+    return -1;
+  }
+
+  domain->default_verify_mode = mode;
+  return 0;
+}
+
+
+pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *domain, pn_transport_t *transport)
+{
+  if (!transport || !domain) return NULL;
+  if (transport->ssl) return transport->ssl;
+
+  pn_ssl_t *ssl = calloc(1, sizeof(pn_ssl_t));
+  if (!ssl) return NULL;
+
+  ssl->domain = domain;
+  domain->ref_count++;
+
+  if (init_ssl_socket(ssl)) {
+    pn_ssl_free(ssl);
+    return NULL;
+  }
+
+  ssl->transport = transport;
+  ssl->process_input = process_input_ssl;
+  ssl->process_output = process_output_ssl;
+  transport->ssl = ssl;
+
+  ssl->trace = (transport->disp) ? transport->disp->trace : PN_TRACE_OFF;
+
+  return ssl;
+}
+
+
 int pn_ssl_allow_unsecured_client(pn_ssl_t *ssl)
 {
   if (ssl) {
-    if (ssl->mode != PN_SSL_MODE_SERVER) {
+    if (ssl->domain && ssl->domain->mode != PN_SSL_MODE_SERVER) {
       _log_error("Cannot permit unsecured clients - not a server.\n");
       return -1;
     }
@@ -362,58 +529,62 @@ int pn_ssl_allow_unsecured_client(pn_ssl
 }
 
 
+
 int pn_ssl_set_peer_authentication(pn_ssl_t *ssl,
                                    const pn_ssl_verify_mode_t mode,
                                    const char *trusted_CAs)
 {
-  if (!ssl) return 0;
-  if (ssl->ssl) {
-    _log_error("Error: attempting to set peer authentication after SSL connection initialized.\n");
-    return -1;
+  if (!ssl) return -1;
+  if (!ssl->domain) return -1;
+  pn_ssl_domain_t *domain = ssl->domain;
+  if (!ssl->ssl) {
+    int rc = init_ssl_socket(ssl);
+    if (rc) return rc;
   }
 
   switch (mode) {
   case PN_SSL_VERIFY_PEER:
 
-    if (!ssl->has_ca_db) {
+    if (!domain->has_ca_db) {
       _log_error("Error: cannot verify peer without a trusted CA configured.\n"
-                 "       Use pn_ssl_set_trusted_ca_db()\n");
+                 "       Use pn_ssl_domain_set_trusted_ca_db()\n");
       return -1;
     }
 
-    if (ssl->mode == PN_SSL_MODE_SERVER) {
+    if (domain->mode == PN_SSL_MODE_SERVER) {
       // openssl requires that server connections supply a list of trusted CAs which is
       // sent to the client
       if (!trusted_CAs) {
         _log_error("Error: a list of trusted CAs must be provided.\n");
         return -1;
       }
-      if (!ssl->has_certificate) {
+      if (!domain->has_certificate) {
       _log_error("Error: Server cannot verify peer without configuring a certificate.\n"
-                 "       Use pn_ssl_set_credentials()\n");
+                 "       Use pn_ssl_domain_set_credentials()\n");
       }
 
+      if (ssl->trusted_CAs) free(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 );
       if (cert_names != NULL)
-        SSL_CTX_set_client_CA_list(ssl->ctx, cert_names);
+        SSL_set_client_CA_list(ssl->ssl, cert_names);
       else {
         _log_error("Unable to process file of trusted CAs: %s\n", trusted_CAs);
         return -1;
       }
     }
 
-    SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
+    SSL_set_verify( ssl->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
     // verify_callback /*?verify callback?*/ );
 #if (OPENSSL_VERSION_NUMBER < 0x00905100L)
-    SSL_CTX_set_verify_depth(ssl->ctx, 1);
+    SSL_set_verify_depth(ssl->ssl, 1);
 #endif
     _log( ssl, "Peer authentication mode set to VERIFY-PEER\n");
     break;
 
   case PN_SSL_ANONYMOUS_PEER:   // hippie free love mode... :)
-    SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_NONE, NULL );
+    SSL_set_verify( ssl->ssl, SSL_VERIFY_NONE, NULL );
     _log( ssl, "Peer authentication mode set to ANONYMOUS-PEER\n");
     break;
 
@@ -433,18 +604,27 @@ int pn_ssl_get_peer_authentication(pn_ss
 {
   if (!ssl) return -1;
 
-  if (mode) *mode = ssl->verify_mode;
+  pn_ssl_verify_mode_t my_mode = ssl->verify_mode;
+  char *my_trusted_CAs = ssl->trusted_CAs;
+
+  if (ssl->verify_mode == PN_SSL_VERIFY_NULL && ssl->domain) {
+    // using the parent domain's values:
+    my_mode = ssl->domain->default_verify_mode;
+    my_trusted_CAs = ssl->domain->default_trusted_CAs;
+  }
+
+  if (mode) *mode = my_mode;
   if (trusted_CAs && trusted_CAs_size && *trusted_CAs_size) {
-    if (ssl->trusted_CAs) {
-      strncpy( trusted_CAs, ssl->trusted_CAs, *trusted_CAs_size );
+    if (my_trusted_CAs) {
+      strncpy( trusted_CAs, my_trusted_CAs, *trusted_CAs_size );
       trusted_CAs[*trusted_CAs_size - 1] = '\0';
-      *trusted_CAs_size = strlen(ssl->trusted_CAs) + 1;
+      *trusted_CAs_size = strlen(my_trusted_CAs) + 1;
     } else {
       *trusted_CAs = '\0';
       *trusted_CAs_size = 0;
     }
   } else if (trusted_CAs_size) {
-    *trusted_CAs_size = (ssl->trusted_CAs) ? strlen(ssl->trusted_CAs) + 1 : 0;
+    *trusted_CAs_size = (my_trusted_CAs) ? strlen(my_trusted_CAs) + 1 : 0;
   }
   return 0;
 }
@@ -480,25 +660,92 @@ bool pn_ssl_get_protocol_name(pn_ssl_t *
 }
 
 
+void pn_ssl_free( pn_ssl_t *ssl)
+{
+  if (!ssl) return;
+  _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);
+
+  free(ssl);
+}
+
+// move data received from the network into the SSL layer
+ssize_t pn_ssl_input(pn_ssl_t *ssl, const char *bytes, size_t available)
+{
+  return ssl->process_input( ssl->transport, bytes, available );
+}
+
+// pull output from the SSL layer and move into network output buffers
+ssize_t pn_ssl_output(pn_ssl_t *ssl, char *buffer, size_t max_size)
+{
+  return ssl->process_output( ssl->transport, buffer, max_size );
+}
+
+
+
+// Deprecated (old non-domain based api)
+int pn_ssl_set_credentials( pn_ssl_t *ssl,
+                            const char *certificate_file,
+                            const char *private_key_file,
+                            const char *password)
+{
+  if (!ssl || !ssl->domain) return -1;
+  if (!ssl->private_domain) {
+    _log_error("Error: use pn_ssl_domain_set_credentials() instead\n");
+    return -1;
+  }
+  if (ssl->ssl) {
+    _log_error("Error: attempting to set credentials while SSL in use.\n");
+    return -1;
+  }
+
+  int rc = pn_ssl_domain_set_credentials( ssl->domain, certificate_file, private_key_file, password );
+  _log( ssl, "Configured local certificate file %s (%d)\n", certificate_file, rc );
+  return rc;
+}
+
+
+// Deprecated (old non-domain based api)
+int pn_ssl_set_trusted_ca_db(pn_ssl_t *ssl,
+                             const char *certificate_db)
+{
+  if (!ssl || !ssl->domain) return -1;
+  if (!ssl->private_domain) {
+    _log_error("Error: use pn_ssl_domain_trusted_ca_db() instead.\n");
+    return -1;
+  }
+  if (ssl->ssl) {
+    _log_error("Error: attempting to set trusted CA db after SSL connection initialized.\n");
+    return -1;
+  }
+
+  int rc = pn_ssl_domain_set_trusted_ca_db( ssl->domain, certificate_db );
+  if (!rc) _log( ssl, "loaded trusted CA database %s\n", certificate_db );
+  return rc;
+}
+
 
+// Deprecated (old non-domain based api)
 int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_mode_t mode)
 {
   if (!ssl) return -1;
-  if (ssl->mode == mode) return 0;      // already set
-  if (ssl->ssl) {
-    _log_error("Unable to change mode once SSL is active.\n");
+  if (!ssl->private_domain) {
+    _log_error("Error: deprecated, use pn_ssl_domain_*() api instead.\n");
     return -1;
   }
+  if (ssl->domain && ssl->domain->mode == mode) return 0;      // already set
 
-  // if changing the mode from the default, must release old context
-  if (ssl->ctx) SSL_CTX_free( ssl->ctx );
+  // if changing modes, must teardown any exising configuration
+  if (ssl->ssl) pn_ssl_free( ssl );
+  if (ssl->domain) pn_ssl_domain_free( ssl->domain );
 
   switch (mode) {
   case PN_SSL_MODE_CLIENT:
     _log( ssl, "Setting up Client SSL object.\n" );
-    ssl->mode = PN_SSL_MODE_CLIENT;
-    ssl->ctx = SSL_CTX_new(SSLv23_client_method());
-    if (!ssl->ctx) {
+    ssl->domain = pn_ssl_domain(PN_SSL_MODE_CLIENT);
+    if (!ssl->domain) {
       _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
       return -1;
     }
@@ -506,9 +753,8 @@ int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_mo
 
   case PN_SSL_MODE_SERVER:
     _log( ssl, "Setting up Server SSL object.\n" );
-    ssl->mode = PN_SSL_MODE_SERVER;
-    ssl->ctx = SSL_CTX_new(SSLv23_server_method());
-    if (!ssl->ctx) {
+    ssl->domain = pn_ssl_domain(PN_SSL_MODE_SERVER);
+    if (!ssl->domain) {
       _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
       return -1;
     }
@@ -518,28 +764,10 @@ int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_mo
     _log_error("Invalid valid for pn_ssl_mode_t: %d\n", mode);
     return -1;
   }
-
-  // by default, allow anonymous ciphers so certificates are not required 'out of the box'
-  if (!SSL_CTX_set_cipher_list( ssl->ctx, CIPHERS_ANONYMOUS )) {
-    _log_ssl_error(ssl, "Failed to set cipher list to %s\n", CIPHERS_ANONYMOUS);
-    return -2;
-  }
-
-  // ditto: by default do not authenticate the peer (can be done by SASL).
-  if (pn_ssl_set_peer_authentication( ssl, PN_SSL_ANONYMOUS_PEER, NULL )) {
-    return -2;
-  }
-
-  DH *dh = get_dh2048();
-  if (dh) {
-    SSL_CTX_set_tmp_dh(ssl->ctx, dh);
-    DH_free(dh);
-    SSL_CTX_set_options(ssl->ctx, SSL_OP_SINGLE_DH_USE);
-  }
-
   return 0;
 }
 
+// Deprecated (old non-domain based api)
 pn_ssl_t *pn_ssl(pn_transport_t *transport)
 {
   if (!transport) return NULL;
@@ -555,6 +783,7 @@ pn_ssl_t *pn_ssl(pn_transport_t *transpo
   pn_ssl_t *ssl = calloc(1, sizeof(pn_ssl_t));
   if (!ssl) return NULL;
 
+  ssl->private_domain = true;
   ssl->transport = transport;
   ssl->process_input = process_input_ssl;
   ssl->process_output = process_output_ssl;
@@ -571,37 +800,6 @@ pn_ssl_t *pn_ssl(pn_transport_t *transpo
 }
 
 
-void pn_ssl_free( pn_ssl_t *ssl)
-{
-  if (!ssl) return;
-  _log( ssl, "SSL socket freed.\n" );
-  if (ssl->bio_ssl) BIO_free(ssl->bio_ssl);
-  if (ssl->ssl) SSL_free(ssl->ssl);
-  else {
-    if (ssl->bio_ssl_io) BIO_free(ssl->bio_ssl_io);
-    if (ssl->bio_net_io) BIO_free(ssl->bio_net_io);
-  }
-  if (ssl->ctx) SSL_CTX_free(ssl->ctx);
-
-  if (ssl->keyfile_pw) free(ssl->keyfile_pw);
-  if (ssl->trusted_CAs) free(ssl->trusted_CAs);
-
-  free(ssl);
-}
-
-// move data received from the network into the SSL layer
-ssize_t pn_ssl_input(pn_ssl_t *ssl, const char *bytes, size_t available)
-{
-  return ssl->process_input( ssl->transport, bytes, available );
-}
-
-// pull output from the SSL layer and move into network output buffers
-ssize_t pn_ssl_output(pn_ssl_t *ssl, char *buffer, size_t max_size)
-{
-  return ssl->process_output( ssl->transport, buffer, max_size );
-}
-
-
 /** Private: */
 
 static int keyfile_pw_cb(char *buf, int size, int rwflag, void *userdata)
@@ -873,7 +1071,7 @@ static int init_ssl_socket( pn_ssl_t *ss
 {
   if (ssl->ssl) return 0;
 
-  ssl->ssl = SSL_new(ssl->ctx);
+  ssl->ssl = SSL_new(ssl->domain->ctx);
   if (!ssl->ssl) {
     _log_error( "SSL socket setup failure.\n" );
     return -1;
@@ -894,7 +1092,7 @@ static int init_ssl_socket( pn_ssl_t *ss
   }
   SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io);
 
-  if (ssl->mode == PN_SSL_MODE_SERVER) {
+  if (ssl->domain->mode == PN_SSL_MODE_SERVER) {
     SSL_set_accept_state(ssl->ssl);
     BIO_set_ssl_mode(ssl->bio_ssl, 0);  // server mode
     _log( ssl, "Server SSL socket created.\n" );
@@ -906,6 +1104,21 @@ static int init_ssl_socket( pn_ssl_t *ss
   return 0;
 }
 
+static void release_ssl_socket( pn_ssl_t *ssl )
+{
+  if (ssl->bio_ssl) BIO_free(ssl->bio_ssl);
+  if (ssl->ssl) SSL_free(ssl->ssl);
+  else {
+    if (ssl->bio_ssl_io) BIO_free(ssl->bio_ssl_io);
+    if (ssl->bio_net_io) BIO_free(ssl->bio_net_io);
+  }
+  ssl->bio_ssl = NULL;
+  ssl->bio_ssl_io = NULL;
+  ssl->bio_net_io = NULL;
+  ssl->ssl = NULL;
+}
+
+
 //////// CLEARTEXT CONNECTIONS
 
 static ssize_t process_input_cleartext(pn_transport_t *transport, const char *input_data, size_t len)
@@ -1010,3 +1223,31 @@ 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 )
+{
+  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;
+}
+
+void pn_ssl_state_free( pn_ssl_state_t *state )
+{
+  SSL_SESSION_free( (SSL_SESSION *)state );
+}

Modified: qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/ssl_stub.c
URL: http://svn.apache.org/viewvc/qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/ssl_stub.c?rev=1417082&r1=1417081&r2=1417082&view=diff
==============================================================================
--- qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/ssl_stub.c (original)
+++ qpid/proton/branches/kgiusti-proton-136/proton-c/src/ssl/ssl_stub.c Tue Dec  4 18:17:34 2012
@@ -104,3 +104,56 @@ bool pn_ssl_get_protocol_name(pn_ssl_t *
   return false;
 }
 
+pn_ssl_domain_t *pn_ssl_domain( pn_ssl_mode_t mode)
+{
+  return NULL;
+}
+void pn_ssl_domain_free( pn_ssl_domain_t *d )
+{
+}
+
+pn_ssl_t *pn_ssl_new( pn_ssl_domain_t *d, pn_transport_t *t)
+{
+  return NULL;
+}
+
+int pn_ssl_domain_set_credentials( pn_ssl_domain_t *domain,
+                               const char *certificate_file,
+                               const char *private_key_file,
+                               const char *password)
+{
+  return -1;
+}
+
+int pn_ssl_domain_set_trusted_ca_db(pn_ssl_domain_t *domain,
+                                const char *certificate_db)
+{
+  return -1;
+}
+
+int pn_ssl_domain_set_default_peer_authentication(pn_ssl_domain_t *domain,
+                                                  const pn_ssl_verify_mode_t mode,
+                                                  const char *trusted_CAs)
+{
+  return -1;
+}
+
+pn_ssl_state_t *pn_ssl_get_state( pn_ssl_t *s)
+{
+  return NULL;
+}
+
+int pn_ssl_resume_state( pn_ssl_t *s, pn_ssl_state_t *t )
+{
+  return -1;
+}
+
+bool pn_ssl_state_resumed_ok( pn_ssl_t *s )
+{
+  return false;
+}
+
+void pn_ssl_state_free( pn_ssl_state_t *s )
+{
+}
+

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=1417082&r1=1417081&r2=1417082&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 Tue Dec  4 18:17:34 2012
@@ -44,11 +44,14 @@ class SslTest(common.Test):
         self.t_server = None
 
     def _pump(self):
+        self._pump2(self.t_client, self.t_server)
+
+    def _pump2(self, client, server):
         while True:
-            out_client = self.t_client.output(1024)
-            out_server = self.t_server.output(1024)
-            if out_client: self.t_server.input(out_client)
-            if out_server: self.t_client.input(out_server)
+            out_client = client.output(1024)
+            out_server = server.output(1024)
+            if out_client: server.input(out_client)
+            if out_server: client.input(out_server)
             if not out_client and not out_server: break
 
     def _testpath(self, file):
@@ -277,3 +280,154 @@ class SslTest(common.Test):
         except TransportException:
             assert True
 
+    def test_domain_server_authentication(self):
+        """ Simple SSL connection with authentication of the server
+        """
+        server_domain = SSLDomain(SSL.MODE_SERVER)
+        server_domain.set_credentials(self._testpath("server-certificate.pem"),
+                                           self._testpath("server-private-key.pem"),
+                                           "server-password")
+        server_transport = Transport()
+        server_ssl = SSL( server_transport, server_domain)
+
+        client_domain = SSLDomain(SSL.MODE_CLIENT)
+        client_domain.set_trusted_ca_db(self._testpath("ca-certificate.pem"))
+        client_domain.set_default_peer_authentication( SSL.VERIFY_PEER )
+        client_transport = Transport()
+        client_ssl = SSL( client_transport, client_domain )
+
+        client_conn = Connection()
+        client_transport.bind(client_conn)
+
+        server_conn = Connection()
+        server_transport.bind(server_conn)
+
+        client_conn.open()
+        server_conn.open()
+        self._pump2( client_transport, server_transport )
+        assert client_ssl.protocol_name() is not None
+        client_conn.close()
+        server_conn.close()
+        self._pump2( client_transport, server_transport )
+
+
+    def test_domain_bad_server_certificate(self):
+        """ A server with a self-signed certificate that is not trusted by the
+        client.  The client should reject the server.
+        """
+
+        server_domain = SSLDomain(SSL.MODE_SERVER)
+        server_domain.set_credentials(self._testpath("bad-server-certificate.pem"),
+                                      self._testpath("bad-server-private-key.pem"),
+                                      "server-password")
+        server_domain.set_default_peer_authentication( SSL.ANONYMOUS_PEER )
+
+        # by default, clients will allow connections to the server with the bad cert
+        client_domain = SSLDomain(SSL.MODE_CLIENT)
+        client_domain.set_trusted_ca_db(self._testpath("ca-certificate.pem"))
+        client_domain.set_default_peer_authentication( SSL.ANONYMOUS_PEER )
+
+        # create a server listener
+        server_transport = Transport()
+        server_ssl = SSL( server_transport, server_domain)
+        server_conn = Connection()
+        server_transport.bind(server_conn)
+
+        # create a client using anonymous
+        bad_client_transport = Transport()
+        bad_client_ssl = SSL( bad_client_transport, client_domain )
+        bad_client_conn = Connection()
+        bad_client_transport.bind(bad_client_conn)
+
+        # client will allow connection to server:
+        bad_client_conn.open()
+        server_conn.open()
+        self._pump2( bad_client_transport, server_transport )
+        assert bad_client_ssl.protocol_name() is not None
+        bad_client_conn.close()
+        server_conn.close()
+
+        # now instantiate new client using same domain, but override to verify
+        # peer:
+        good_client_transport = Transport()
+        good_client_ssl = SSL( good_client_transport, client_domain )
+        good_client_ssl.set_peer_authentication( SSL.VERIFY_PEER )
+        good_client_conn = Connection()
+        good_client_transport.bind(good_client_conn)
+
+        server_transport = Transport()
+        server_ssl = SSL( server_transport, server_domain)
+        server_conn = Connection()
+        server_transport.bind(server_conn)
+
+        good_client_conn.open()
+        server_conn.open()
+        try:
+            self._pump2( good_client_transport, server_transport )
+            assert False, "Client failed to reject bad certificate."
+        except TransportException, e:
+            pass
+        good_client_conn.close()
+        server_conn.close()
+
+    def test_session_resume(self):
+        """ Test resume of client session.
+        """
+        # server config
+        server_domain = SSLDomain(SSL.MODE_SERVER)
+        server_domain.set_credentials(self._testpath("server-certificate.pem"),
+                                           self._testpath("server-private-key.pem"),
+                                           "server-password")
+        # client config
+        client_domain = SSLDomain(SSL.MODE_CLIENT)
+        client_domain.set_trusted_ca_db(self._testpath("ca-certificate.pem"))
+        client_domain.set_default_peer_authentication( SSL.VERIFY_PEER )
+
+        # setup first client session
+        client_transport = Transport()
+        client_ssl = SSL( client_transport, client_domain )
+        client_conn = Connection()
+        client_transport.bind(client_conn)
+
+        # create server session to connect to
+        server_transport = Transport()
+        server_ssl = SSL( server_transport, server_domain)
+        server_conn = Connection()
+        server_transport.bind(server_conn)
+
+        #client_transport.trace(Transport.TRACE_DRV)
+
+        # bring up the connection and store its state
+        client_conn.open()
+        server_conn.open()
+        self._pump2( client_transport, server_transport )
+        assert client_ssl.protocol_name() is not None
+        ssl_state = client_ssl.get_state()
+        client_conn.close()
+        server_conn.close()
+        self._pump2( client_transport, server_transport )
+
+        # now create a new set of connections
+        client_transport = Transport()
+        client_ssl = SSL( client_transport, client_domain )
+        client_conn = Connection()
+        client_transport.bind(client_conn)
+
+        server_transport = Transport()
+        server_ssl = SSL( server_transport, server_domain)
+        server_conn = Connection()
+        server_transport.bind(server_conn)
+
+        client_ssl.resume_state( ssl_state )
+        client_conn.open()
+        server_conn.open()
+        self._pump2( client_transport, server_transport )
+        assert client_ssl.protocol_name() is not None
+        assert client_ssl.state_resumed_ok()
+        #newstate = client_ssl.get_state()
+
+        del ssl_state
+        client_conn.close()
+        server_conn.close()
+        self._pump2( client_transport, server_transport )
+



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org