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-unsubscr...@qpid.apache.org For additional commands, e-mail: commits-h...@qpid.apache.org