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/09/19 02:23:00 UTC

svn commit: r1387415 - /qpid/proton/branches/driver_abstraction/proton-c/src/ssl/openssl.c

Author: kgiusti
Date: Wed Sep 19 00:22:59 2012
New Revision: 1387415

URL: http://svn.apache.org/viewvc?rev=1387415&view=rev
Log:
checkpoint

Modified:
    qpid/proton/branches/driver_abstraction/proton-c/src/ssl/openssl.c

Modified: qpid/proton/branches/driver_abstraction/proton-c/src/ssl/openssl.c
URL: http://svn.apache.org/viewvc/qpid/proton/branches/driver_abstraction/proton-c/src/ssl/openssl.c?rev=1387415&r1=1387414&r2=1387415&view=diff
==============================================================================
--- qpid/proton/branches/driver_abstraction/proton-c/src/ssl/openssl.c (original)
+++ qpid/proton/branches/driver_abstraction/proton-c/src/ssl/openssl.c Wed Sep 19 00:22:59 2012
@@ -34,7 +34,6 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <assert.h>
-//#include <sys/socket.h>
 
 
 /** @file
@@ -64,11 +63,13 @@ struct pn_ssl_t {
   BIO *bio_ssl_io;      // SSL "half" of network-facing BIO
   BIO *bio_net_io;      // socket-side "half" of network-facing BIO
   bool read_stalled;  // SSL has data to read, but client buffer is full.
-  bool ssl_closed;      // SSL socket has closed
   bool ssl_shutdown;    // BIO_ssl_shutdown() called on socket.
-  ssize_t app_closed;   // error code returned by upper layer
-
+  ssize_t app_closed;   // error code returned by upper layer on close
+  ssize_t ssl_closed;   // SSL socket has closed (use upper layer code)
 
+  bool read_blocked;    // SSL blocked until more network data is read
+  bool write_blocked;   // SSL blocked until data is written to network
+  
   // buffers for holding I/O from "applications" above SSL
 #define APP_BUF_SIZE    PN_CONNECTOR_IO_BUF_SIZE
   char outbuf[APP_BUF_SIZE];
@@ -93,6 +94,7 @@ static ssize_t process_output_cleartext(
 static ssize_t process_input_unknown(pn_transport_t *transport, char *input_data, size_t len);
 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 * );
 
 
 // @todo: used to avoid littering the code with calls to printf...
@@ -178,6 +180,10 @@ int pn_ssl_set_credentials( pn_ssl_t *ss
                             const char *password)
 {
   if (!ssl) return 0;
+  if (ssl->ssl) {
+    _log_error("Error: attempting to set credentials after SSL connection initialized.\n");
+    return -1;
+  }
 
   if (SSL_CTX_use_certificate_chain_file(ssl->ctx, certificate_file) != 1) {
     _log_error("SSL_CTX_use_certificate_chain_file( %s ) failed\n", certificate_file);
@@ -204,6 +210,10 @@ int pn_ssl_set_trusted_ca_db(pn_ssl_t *s
                              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;
+  }
 
   // certificates can be either a file or a directory, which determines how it is passed
   // to SSL_CTX_load_verify_locations()
@@ -254,6 +264,10 @@ int pn_ssl_set_peer_authentication(pn_ss
                                    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;
+  }
 
   switch (mode) {
   case PN_SSL_VERIFY_PEER:
@@ -270,7 +284,7 @@ int pn_ssl_set_peer_authentication(pn_ss
       STACK_OF(X509_NAME) *cert_names;
       cert_names = SSL_load_client_CA_file( ssl->trusted_CAs );
       if (cert_names != NULL)
-        SSL_set_client_CA_list(ssl->ssl, cert_names);
+        SSL_CTX_set_client_CA_list(ssl->ctx, cert_names);
       else {
         _log_error("Unable to process file of trusted CAs: %s\n", trusted_CAs);
         return -1;
@@ -352,31 +366,6 @@ pn_ssl_t *pn_ssl_server(pn_transport_t *
   ssl->verify_mode = PN_SSL_NO_VERIFY_PEER;
   SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_NONE, NULL );        // default: no client authentication
 
-  ssl->ssl = SSL_new(ssl->ctx);
-  if (!ssl->ssl) {
-    _log_error( "SSL socket setup failure.\n" );
-    pn_ssl_free(ssl);
-    return NULL;
-  }
-  SSL_set_accept_state(ssl->ssl);
-
-  // now layer a BIO over the SSL socket
-  ssl->bio_ssl = BIO_new(BIO_f_ssl());
-  if (!ssl->bio_ssl) {
-    _log_error( "BIO setup failure.\n" );
-    pn_ssl_free(ssl);
-    return NULL;
-  }
-  (void)BIO_set_ssl(ssl->bio_ssl, ssl->ssl, BIO_NOCLOSE);
-
-  // create the "lower" BIO "pipe", and attach it below the SSL layer
-  if (!BIO_new_bio_pair(&ssl->bio_ssl_io, 0, &ssl->bio_net_io, 0)) {
-    _log_error( "BIO setup failure.\n" );
-    pn_ssl_free(ssl);
-    return NULL;
-  }
-  SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io);
-
   ssl->mode = SSL_MODE_SERVER;
   ssl->transport = transport;
   ssl->process_input = process_input_ssl;
@@ -385,7 +374,7 @@ pn_ssl_t *pn_ssl_server(pn_transport_t *
 
   ssl->trace = PN_TRACE_OFF;
 
-  _log( ssl, "Server SSL socket created.\n" );
+  _log( ssl, "Setting up Server SSL connection.\n" );
   return ssl;
 }
 
@@ -421,31 +410,6 @@ pn_ssl_t *pn_ssl_client(pn_transport_t *
   SSL_CTX_set_verify_depth(ssl->ctx, 1);
 #endif
 
-  ssl->ssl = SSL_new(ssl->ctx);
-  if (!ssl->ssl) {
-    _log_error( "SSL socket setup failure.\n" );
-    pn_ssl_free(ssl);
-    return NULL;
-  }
-  SSL_set_connect_state(ssl->ssl);
-
-  // now layer a BIO over the SSL socket
-  ssl->bio_ssl = BIO_new(BIO_f_ssl());
-  if (!ssl->bio_ssl) {
-    _log_error( "BIO setup failure.\n" );
-    pn_ssl_free(ssl);
-    return NULL;
-  }
-  (void)BIO_set_ssl(ssl->bio_ssl, ssl->ssl, BIO_NOCLOSE);
-
-  // create the "lower" BIO "pipe", and attach it below the SSL layer
-  if (!BIO_new_bio_pair(&ssl->bio_ssl_io, 0, &ssl->bio_net_io, 0)) {
-    _log_error( "BIO setup failure.\n" );
-    pn_ssl_free(ssl);
-    return NULL;
-  }
-  SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io);
-
   ssl->mode = SSL_MODE_CLIENT;
 
   ssl->transport = transport;
@@ -455,7 +419,7 @@ pn_ssl_t *pn_ssl_client(pn_transport_t *
 
   ssl->trace = PN_TRACE_OFF;
 
-  _log( ssl, "Client SSL socket created.\n" );
+  _log( ssl, "Setting up Client SSL connection.\n" );
   return ssl;
 }
 
@@ -498,11 +462,10 @@ static int keyfile_pw_cb(char *buf, int 
 }
 
 
-
-
-static int start_ssl_shutdown( pn_ssl_t *ssl )
+int start_ssl_shutdown( pn_ssl_t *ssl )
 {
   if (!ssl->ssl_shutdown) {
+    _log(ssl, "Shutting down SSL connection...\n");
     ssl->ssl_shutdown = true;
     BIO_ssl_shutdown( ssl->bio_ssl );
   }
@@ -521,201 +484,243 @@ static int setup_ssl_connection( pn_ssl_
 
 //////// SSL Connections
 
-// transfer data between the SSL socket and the upper layer.  Returns true if any work was
-// done.  This routine modifies the app buffers (outbuf and inbuf), and possibly sets the
-// ssl_closed or app_closed flags.
-static bool do_socket_io( pn_ssl_t *ssl )
-{
-  pn_transport_t *transport = ssl->transport;
-  size_t total = 0;
-  size_t activity;
-
-  do {
-    activity = 0;
-
-    // get outgoing data from app layer
-
-    if (!ssl->app_closed) {
-      while (ssl->out_count < APP_BUF_SIZE) {
-        ssize_t app_bytes = transport->process_output(transport, &ssl->outbuf[ssl->out_count], ssl->out_count);
-        if (app_bytes > 0) {
-          ssl->out_count += app_bytes;
-          activity += app_bytes;
-        } else {
-          if (app_bytes < 0) {
-            _log(ssl, "Application layer closed: %d (out_count=%d)\n", (int) app_bytes, (int) ssl->out_count);
-            ssl->app_closed = app_bytes;
-            if (app_bytes == PN_EOS) {
-              if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
-                pn_dispatcher_trace(transport->disp, 0, "-> EOS\n");
-            } else {
-              if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
-                pn_dispatcher_trace(transport->disp, 0, "-> EOS (%zi) %s\n", app_bytes,
-                                    pn_error_text(transport->error));
-            }
-          }
-          break;
-        }
-      }
-    }
 
-    // write outgoing data to socket
+// take data from the network, and pass it into SSL.  Attempt to read decrypted data from
+// SSL socket and pass it to the application.
+static ssize_t process_input_ssl( pn_transport_t *transport, char *input_data, size_t available)
+{
+  pn_ssl_t *ssl = transport->ssl;
+  if (!ssl) return PN_ERR;
+  if (ssl->ssl == NULL && init_ssl_socket(ssl)) return PN_ERR;
 
-    if (!ssl->ssl_closed) {
-      char *data = ssl->outbuf;
-      while (ssl->out_count > 0) {
-        int written = BIO_write( ssl->bio_ssl, data, ssl->out_count );
-        if (written > 0) {
-          data += written;
-          ssl->out_count -= written;
-          activity += written;
-        } else if (!BIO_should_retry(ssl->bio_ssl)) {
-          _log(ssl, "Write to SSL socket failed - SSL connection closed!!\n");
-          ssl->ssl_closed = true;
-          break;
-        }
-      }
-      if (!ssl->ssl_closed && ssl->out_count > 0 && data != ssl->outbuf)
-        memmove( ssl->outbuf, data, ssl->out_count );
-    }
+  /* if (ssl->write_blocked) { */
+  /*   _log(ssl, "SSL write_blocked, ignoring input data\n" ); */
+  /*   return 0; */
+  /* } */
 
-    if (ssl->ssl_closed) {
-      ssl->out_count = 0;       // cannot write to socket, so erase app output data
+  ssize_t consumed = 0;
+
+  // Write to network bio as much as possible, consuming bytes/available
+
+  if (available) {
+    int written = BIO_write( ssl->bio_net_io, input_data, available );
+    if (written > 0) {
+      input_data += written;
+      available -= written;
+      consumed += written;
+      ssl->read_blocked = false;
+      _log( ssl, "Wrote %d bytes to BIO Layer, %d left over\n", written, available );
     }
+  }
 
-    // read incoming data from socket
+  // process any work available at the SSL socket
 
-    if (!ssl->ssl_closed) {
-      while ((APP_BUF_SIZE - ssl->in_count) > 0) {
-        int written = BIO_read( ssl->bio_ssl, &ssl->inbuf[ssl->in_count], APP_BUF_SIZE - ssl->in_count );
-        if (written > 0) {
-          ssl->in_count += written;
-          activity += written;
-        } else if (!BIO_should_retry(ssl->bio_ssl)) {
+  if (!ssl->ssl_closed) {
+    int pending = BIO_pending(ssl->bio_ssl);
+    int available = pn_min( (APP_BUF_SIZE - ssl->in_count), pending );
+    while (available > 0) {
+      int written = BIO_read( ssl->bio_ssl, &ssl->inbuf[ssl->in_count], available );
+      if (written > 0) {
+        ssl->in_count += written;
+        _log( ssl, "Read %d bytes from socket for app\n", written );
+      } else {
+        if (!BIO_should_retry(ssl->bio_ssl)) {
           _log(ssl, "Read from SSL socket failed - SSL connection closed!!\n");
-          ssl->ssl_closed = true;
-          break;
+          ssl->ssl_closed = (ssl->app_closed) ? ssl->app_closed : PN_EOS;
+          start_ssl_shutdown(ssl);
+        } else {
+          if (BIO_should_write( ssl->bio_ssl )) {
+            ssl->write_blocked = true;
+            _log(ssl, "Detected write-blocked\n");
+          }
+          if (BIO_should_read( ssl->bio_ssl )) {
+            ssl->read_blocked = true;
+            _log(ssl, "Detected read-blocked\n");
+          }
         }
+        break;
       }
+      pending = BIO_pending(ssl->bio_ssl);
+      available = pn_min( (APP_BUF_SIZE - ssl->in_count), pending );
     }
+  }
 
-    // write incoming data to app layer
+  // write incoming data to app layer
 
-    if (!ssl->app_closed) {
-      char *data = ssl->inbuf;
-      while (ssl->in_count > 0) {
-        ssize_t consumed = transport->process_input(transport, data, ssl->in_count);
-        if (consumed > 0) {
-          ssl->in_count -= consumed;
-          data += consumed;
-          activity += consumed;
-        } else {
-          if (consumed < 0) {
-            _log(ssl, "Application layer closed: %d (in_count=%d)\n", (int) consumed, (int)ssl->in_count);
-            ssl->app_closed = consumed;
-            if (consumed == PN_EOS) {
-              if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
-                pn_dispatcher_trace(transport->disp, 0, "<- EOS\n");
-            } else {
-              pn_dispatcher_trace(transport->disp, 0, "ERROR[%i] %s\n",
-                                  pn_error_code(transport->error),
-                                  pn_error_text(transport->error));
-            }
+  if (ssl->app_closed) {
+    ssl->in_count = 0;        // cannot accept more input, drop it
+  } else {
+    char *data = ssl->inbuf;
+    while (ssl->in_count > 0) {
+      ssize_t consumed = transport->process_input(transport, data, ssl->in_count);
+      if (consumed > 0) {
+        ssl->in_count -= consumed;
+        data += consumed;
+        _log( ssl, "Application consumed %d bytes from peer\n", (int) consumed );
+      } else {
+        if (consumed < 0) {
+          _log(ssl, "Application layer closed: %d (in_count=%d)\n", (int) consumed, (int)ssl->in_count);
+          ssl->app_closed = consumed;
+          if (consumed == PN_EOS) {
+            if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
+              pn_dispatcher_trace(transport->disp, 0, "<- EOS\n");
+          } else {
+            pn_dispatcher_trace(transport->disp, 0, "ERROR[%i] %s\n",
+                                pn_error_code(transport->error),
+                                pn_error_text(transport->error));
           }
-          break;
         }
+        break;
       }
-      if (!ssl->app_closed && ssl->in_count > 0 && data != ssl->inbuf)
-        memmove( ssl->inbuf, data, ssl->in_count );
     }
+    if (!ssl->app_closed && ssl->in_count > 0 && data != ssl->inbuf)
+      memmove( ssl->inbuf, data, ssl->in_count );
+  }
 
-    if (ssl->app_closed) {
-      ssl->in_count = 0;        // cannot accept more input, drop it
-    }
-
-    total += activity;
-
-  } while (activity);
+  if (consumed == 0 && ssl->ssl_closed && BIO_pending(ssl->bio_net_io) == 0) {
+    consumed = ssl->app_closed;
+  }
 
-  return total > 0 ? true : false;
+  _log(ssl, "process_input_ssl() returning %d\n", (int) consumed);
+  return consumed;
 }
 
-
-static ssize_t process_input_ssl( pn_transport_t *transport, char *input_data, size_t available)
+static ssize_t process_output_ssl( pn_transport_t *transport, char *buffer, size_t max_len)
 {
   pn_ssl_t *ssl = transport->ssl;
   if (!ssl) return PN_ERR;
+  if (ssl->ssl == NULL && init_ssl_socket(ssl)) return PN_ERR;
 
-  ssize_t consumed = 0;
-  bool activity;
+  /* if (ssl->read_blocked) { */
+  /*   _log(ssl, "SSL read_blocked, skipping output data\n" ); */
+  /*   return 0; */
+  /* } */
 
-  do {
+  ssize_t written = 0;
 
-    activity = false;
+  // first, get any pending application output, if possible
 
-    // Write to network bio as much as possible, consuming bytes/available
-    while (available) {
-      int written = BIO_write( ssl->bio_net_io, input_data, available );
-      if (written < 1) break;
-      input_data += written;
-      available -= written;
-      consumed += written;
-      activity = true;
+  if (!ssl->app_closed) {
+    while (ssl->out_count < APP_BUF_SIZE) {
+      ssize_t app_bytes = transport->process_output(transport, &ssl->outbuf[ssl->out_count], APP_BUF_SIZE - ssl->out_count);
+      if (app_bytes > 0) {
+        ssl->out_count += app_bytes;
+        _log( ssl, "Gathered %d bytes from app to send to peer\n", app_bytes );
+      } else {
+        if (app_bytes < 0) {
+          _log(ssl, "Application layer closed: %d (out_count=%d)\n", (int) app_bytes, (int) ssl->out_count);
+          ssl->app_closed = app_bytes;
+          if (app_bytes == PN_EOS) {
+            if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
+              pn_dispatcher_trace(transport->disp, 0, "-> EOS\n");
+          } else {
+            if (transport->disp->trace & (PN_TRACE_RAW | PN_TRACE_FRM))
+              pn_dispatcher_trace(transport->disp, 0, "-> EOS (%zi) %s\n", app_bytes,
+                                  pn_error_text(transport->error));
+          }
+        }
+        break;
+      }
     }
+  }
 
-    // process any work available at the SSL socket or application
+  // now push any pending app data into the socket
 
-    if (do_socket_io(ssl))
-      activity = true;
+  if (ssl->ssl_closed) {
+    ssl->out_count = 0;       // cannot write to socket, so erase app output data
+  } else {
+    char *data = ssl->outbuf;
+    while (ssl->out_count > 0) {
+      int written = BIO_write( ssl->bio_ssl, data, ssl->out_count );
+      if (written > 0) {
+        data += written;
+        ssl->out_count -= written;
+        _log( ssl, "Wrote %d bytes from app to socket\n", written );
+      } else {
+        if (!BIO_should_retry(ssl->bio_ssl)) {
+          _log(ssl, "Write to SSL socket failed - SSL connection closed!!\n");
+          ssl->ssl_closed = (ssl->app_closed) ? ssl->app_closed : PN_EOS;
+          start_ssl_shutdown(ssl);
+        } else {
+          if (BIO_should_read( ssl->bio_ssl )) {
+            ssl->read_blocked = true;
+            _log(ssl, "Detected read-blocked\n");
+          }
+          if (BIO_should_write( ssl->bio_ssl )) {
+            ssl->write_blocked = true;
+            _log(ssl, "Detected write-blocked\n");
+          }
+        }
+        break;
+      }
+    }
 
-  } while (activity);
+    if (ssl->out_count == 0) {
+      if (ssl->app_closed) start_ssl_shutdown(ssl);
+    } else if (data != ssl->outbuf) {
+      memmove( ssl->outbuf, data, ssl->out_count );
+    }
+  }
 
-  if (consumed == 0 && ssl->ssl_closed && ssl->app_closed)
-    return ssl->app_closed;
+  // read from the network bio as much as possible, filling the buffer
+  if (max_len) {
+    int available = BIO_read( ssl->bio_net_io, buffer, max_len );
+    if (available > 0) {
+      max_len -= available;
+      buffer += available;
+      written += available;
+      ssl->write_blocked = false;
+      _log( ssl, "Read %d bytes from BIO Layer\n", available );
+    }
+  }
 
-  _log(ssl, "Processed %d bytes from transport input.\n", (int) consumed );
-  return consumed;
+  // once the app closes, and we drain any data it has written, we can shutdown the SSL
+  // connection cleanly.
+
+  if (written == 0 && ssl->ssl_closed && BIO_pending(ssl->bio_net_io) == 0) {
+    written = ssl->ssl_closed;
+  }
+  _log(ssl, "process_output_ssl() returning %d\n", (int) written);
+  return written;
 }
 
-static ssize_t process_output_ssl( pn_transport_t *transport, char *buffer, size_t max_len)
+static int init_ssl_socket( pn_ssl_t *ssl )
 {
-  pn_ssl_t *ssl = transport->ssl;
-  if (!ssl) return PN_ERR;
+  if (ssl->ssl) return 0;
 
-  ssize_t written = 0;
-  bool activity;
-
-  do {
-    activity = false;
+  ssl->ssl = SSL_new(ssl->ctx);
+  if (!ssl->ssl) {
+    _log_error( "SSL socket setup failure.\n" );
+    return -1;
+  }
 
-    // process any work available at the SSL socket or application
-    if (do_socket_io(ssl))
-      activity = true;
-
-    // read from the network bio as much as possible, filling the buffer
-    while (max_len) {
-      int available = BIO_read( ssl->bio_net_io, buffer, max_len );
-      if (available < 1) break;
-      max_len -= available;
-      buffer += available;
-      written += available;
-      activity = true;
-    }
-  } while (activity);
+  // now layer a BIO over the SSL socket
+  ssl->bio_ssl = BIO_new(BIO_f_ssl());
+  if (!ssl->bio_ssl) {
+    _log_error( "BIO setup failure.\n" );
+    return -1;
+  }
+  (void)BIO_set_ssl(ssl->bio_ssl, ssl->ssl, BIO_NOCLOSE);
 
-  // if the app is closed, and we've written any remaining app output to the socket, then
-  // start the SSL shutdown handshake
-  if (!ssl->ssl_shutdown && ssl->app_closed && ssl->out_count == 0) {
-    start_ssl_shutdown(ssl);
+  // create the "lower" BIO "pipe", and attach it below the SSL layer
+  if (!BIO_new_bio_pair(&ssl->bio_ssl_io, 0, &ssl->bio_net_io, 0)) {
+    _log_error( "BIO setup failure.\n" );
+    return -1;
   }
+  SSL_set_bio(ssl->ssl, ssl->bio_ssl_io, ssl->bio_ssl_io);
 
-  if (written == 0 && ssl->ssl_closed && ssl->app_closed)
-    return ssl->app_closed;
-  _log(ssl, "Created %d bytes for transport output.\n", (int) written );
-  return written;
+  if (ssl->mode == 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" );
+  } else {      // client mode
+    SSL_set_connect_state(ssl->ssl);
+    BIO_set_ssl_mode(ssl->bio_ssl, 1);  // client mode
+    _log( ssl, "Client SSL socket created.\n" );
+  }
+  return 0;
 }
 
-
 //////// CLEARTEXT CONNECTIONS
 
 static ssize_t process_input_cleartext(pn_transport_t *transport, char *input_data, size_t len)



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