Author: kgiusti
Date: Tue Sep 18 13:08:56 2012
New Revision: 1387131
URL: http://svn.apache.org/viewvc?rev=1387131&view=rev
Log:
checkpoint
Modified:
qpid/proton/branches/driver_abstraction/proton-c/include/proton/ssl.h
qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine-internal.h
qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine.c
qpid/proton/branches/driver_abstraction/proton-c/src/ssl/openssl.c
qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl-internal.h
qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl_stub.c
Modified: qpid/proton/branches/driver_abstraction/proton-c/include/proton/ssl.h
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/driver_abstraction/proton-c/include/proton/ssl.h?rev=1387131&r1=1387130&r2=1387131&view=diff
==============================================================================
--- qpid/proton/branches/driver_abstraction/proton-c/include/proton/ssl.h
(original)
+++ qpid/proton/branches/driver_abstraction/proton-c/include/proton/ssl.h Tue
Sep 18 13:08:56 2012
@@ -133,7 +133,7 @@ int pn_ssl_set_peer_authentication(pn_ss
* @param[out] trusted_CAs set to a buffer to hold the path to the database of
trusted CAs
* that the server will advertise to the peer client. If NULL, the path will
not be
* returned.
- * @param[in,out] trusted_CAs_size on input, set to the number of octets in
trusted_CAs,
+ * @param[in,out] trusted_CAs_size on input set to the number of octets in
trusted_CAs.
* on output, set to the number of octets needed to hold the value of
trusted_CAs plus a
* null byte. @return 0 on success
*/
Modified:
qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine-internal.h
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine-internal.h?rev=1387131&r1=1387130&r2=1387131&view=diff
==============================================================================
---
qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine-internal.h
(original)
+++
qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine-internal.h
Tue Sep 18 13:08:56 2012
@@ -87,12 +87,14 @@ typedef struct {
#define SCRATCH (1024)
#include <proton/sasl.h>
+#include <proton/ssl.h>
struct pn_transport_t {
ssize_t (*process_input)(pn_transport_t *, char *, size_t);
ssize_t (*process_output)(pn_transport_t *, char *, size_t);
size_t header_count;
pn_sasl_t *sasl;
+ pn_ssl_t *ssl;
pn_connection_t *connection;
pn_dispatcher_t *disp;
bool open_sent;
Modified: qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine.c
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine.c?rev=1387131&r1=1387130&r2=1387131&view=diff
==============================================================================
--- qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine.c
(original)
+++ qpid/proton/branches/driver_abstraction/proton-c/src/engine/engine.c Tue
Sep 18 13:08:56 2012
@@ -30,6 +30,7 @@
#include <stdio.h>
#include "../sasl/sasl-internal.h"
+#include "../ssl/ssl-internal.h"
// delivery buffers
@@ -229,6 +230,7 @@ void pn_transport_free(pn_transport_t *t
{
if (!transport) return;
+ pn_ssl_free(transport->ssl);
pn_sasl_free(transport->sasl);
pn_dispatcher_free(transport->disp);
for (int i = 0; i < transport->session_capacity; i++) {
@@ -678,6 +680,7 @@ void pn_transport_init(pn_transport_t *t
transport->process_output = pn_output_write_amqp_header;
transport->header_count = 0;
transport->sasl = NULL;
+ transport->ssl = NULL;
transport->disp = pn_dispatcher(0, transport);
pn_dispatcher_action(transport->disp, OPEN, "OPEN", pn_do_open);
@@ -1426,8 +1429,13 @@ ssize_t pn_input(pn_transport_t *transpo
size_t consumed = 0;
+ const bool use_ssl = transport->ssl != NULL;
while (true) {
- ssize_t n = transport->process_input(transport, bytes + consumed,
available - consumed);
+ ssize_t n;
+ if (use_ssl)
+ n = pn_ssl_input( transport->ssl, bytes + consumed, available -
consumed);
+ else
+ n = transport->process_input(transport, bytes + consumed, available -
consumed);
if (n > 0) {
consumed += n;
if (consumed >= available) {
@@ -1988,8 +1996,14 @@ ssize_t pn_output(pn_transport_t *transp
size_t total = 0;
+ const bool use_ssl = transport->ssl != NULL;
+
while (size - total > 0) {
- ssize_t n = transport->process_output(transport, bytes + total, size -
total);
+ ssize_t n;
+ if (use_ssl)
+ n = pn_ssl_output( transport->ssl, bytes + total, size - total);
+ else
+ n = transport->process_output(transport, bytes + total, size - total);
if (n > 0) {
total += n;
} else if (n == 0) {
@@ -2016,6 +2030,7 @@ ssize_t pn_output(pn_transport_t *transp
void pn_trace(pn_transport_t *transport, pn_trace_t trace)
{
if (transport->sasl) pn_sasl_trace(transport->sasl, trace);
+ if (transport->ssl) pn_ssl_trace(transport->ssl, trace);
transport->disp->trace = trace;
}
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=1387131&r1=1387130&r2=1387131&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 Tue Sep
18 13:08:56 2012
@@ -21,7 +21,10 @@
#define _POSIX_C_SOURCE 1
-#include <proton/driver.h>
+#include <proton/ssl.h>
+#include "./ssl-internal.h"
+#include <proton/engine.h>
+#include "../engine/engine-internal.h"
#include "../driver-internal.h"
#include "../util.h"
@@ -31,52 +34,66 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
-#include <sys/socket.h>
+//#include <sys/socket.h>
/** @file
* SSL/TLS support API.
*
- * This file contains stub implementations of the SSL/TLS API. This
implementation is
- * used if there is no SSL/TLS support in the system's environment.
+ * This file contains an OpenSSL-based implemention of the SSL/TLS API.
*/
static int ssl_initialized;
-struct pn_listener_ssl_t {
- SSL_CTX *ctx;
- bool allow_unsecured;
- bool ca_db;
- char *keyfile_pw;
-};
-typedef struct pn_listener_ssl_t pn_listener_ssl_t;
-
+typedef enum { SSL_MODE_CLIENT, SSL_MODE_SERVER } ssl_mode_t;
+typedef enum { UNKNOWN_CONNECTION, SSL_CONNECTION, CLEAR_CONNECTION }
connection_mode_t;
-struct pn_connector_ssl_t {
+struct pn_ssl_t {
+ SSL_CTX *ctx;
+ SSL *ssl;
+ ssl_mode_t mode;
+ bool allow_unsecured;
+ bool ca_db; // true when CA database configured
+ char *keyfile_pw;
+ pn_ssl_verify_mode_t verify_mode; // NEED INIT
+ char *trusted_CAs;
+
+ pn_transport_t *transport;
+
+ BIO *bio_ssl; // i/o from/to SSL socket layer
+ 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
+
+
+ // buffers for holding I/O from "applications" above SSL
+#define APP_BUF_SIZE PN_CONNECTOR_IO_BUF_SIZE
+ char outbuf[APP_BUF_SIZE];
+ size_t out_count;
+ char inbuf[APP_BUF_SIZE];
+ size_t in_count;
+
+ // process cleartext i/o "above" the SSL layer
+ ssize_t (*process_input)(pn_transport_t *, char *, size_t);
+ ssize_t (*process_output)(pn_transport_t *, char *, size_t);
- enum { SSL_CLIENT, SSL_SERVER } mode;
- SSL_CTX *ctx; // NULL if mode=SSL_SERVER - uses listener's ctx
- SSL *ssl;
- BIO *sbio;
- char *keyfile_pw;
- bool read_stalled; // SSL has data to read, but client buffer is full.
+ pn_trace_t trace;
};
-typedef struct pn_connector_ssl_t pn_connector_ssl_t;
+
/* */
static int keyfile_pw_cb(char *buf, int size, int rwflag, void *userdata);
-static int configure_ca_database(SSL_CTX *ctx, const char *certificate_db,
pn_trace_t);
-static int start_check_for_ssl( pn_connector_t *client );
-static int handle_check_for_ssl( pn_connector_t *client );
-static int start_ssl_connect(pn_connector_t *client);
-static int handle_ssl_connect( pn_connector_t *client );
-static int start_ssl_accept(pn_connector_t *client);
-static int handle_ssl_accept(pn_connector_t *client);
-static int start_ssl_connection_up( pn_connector_t *c );
-static int handle_ssl_connection_up( pn_connector_t *c );
-static int start_clear_connected( pn_connector_t *c );
-static int start_ssl_shutdown( pn_connector_t *c );
-static int handle_ssl_shutdown( pn_connector_t *c );
+static ssize_t process_input_ssl( pn_transport_t *transport, char *input_data,
size_t len);
+static ssize_t process_output_ssl( pn_transport_t *transport, char
*input_data, size_t len);
+static ssize_t process_input_cleartext(pn_transport_t *transport, char
*input_data, size_t len);
+static ssize_t process_output_cleartext(pn_transport_t *transport, char
*buffer, size_t max_len);
+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 );
+
// @todo: used to avoid littering the code with calls to printf...
static void _log_error(const char *fmt, ...)
@@ -88,9 +105,9 @@ static void _log_error(const char *fmt,
}
// @todo: used to avoid littering the code with calls to printf...
-static void _log(pn_trace_t filter, const char *fmt, ...)
+static void _log(pn_ssl_t *ssl, const char *fmt, ...)
{
- if (PN_TRACE_DRV & filter) {
+ if (PN_TRACE_DRV & ssl->trace) {
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
@@ -101,7 +118,7 @@ static void _log(pn_trace_t filter, cons
// @todo replace with a "reasonable" default (?), allow application to
register its own
// callback.
-#if 1
+#if 0
static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
{
fprintf(stderr, "VERIFY_CALLBACK: pre-verify-ok=%d\n", preverify_ok);
@@ -149,290 +166,320 @@ static int verify_callback(int preverify
}
#endif
-/** Public API - visible to application code */
-int pn_listener_ssl_server_init(pn_listener_t *listener,
- const char *certificate_file,
- const char *private_key_file,
- const char *password,
- const char *certificate_db)
-{
- listener->ssl = calloc(1, sizeof(pn_listener_ssl_t));
- // note: see pn_listener_free_ssl for cleanup/deallocation
- if (!listener->ssl) {
- _log_error("calloc() failed: %s\n", strerror(errno));
- return -1;
- }
- pn_listener_ssl_t *impl = listener->ssl;
-
- if (!ssl_initialized) {
- ssl_initialized = 1;
- SSL_library_init();
- SSL_load_error_strings();
- }
- impl->ctx = SSL_CTX_new(SSLv23_server_method());
- if (!impl->ctx) {
- _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
- return -2;
- }
- if (SSL_CTX_use_certificate_chain_file(impl->ctx, certificate_file) != 1) {
- _log_error("SSL_CTX_use_certificate_chain_file( %s ) failed\n",
certificate_file);
- return -3;
- }
+/** Public API - visible to application code */
- if (password) {
- impl->keyfile_pw = pn_strdup(password); // @todo: obfuscate me!!!
- SSL_CTX_set_default_passwd_cb(impl->ctx, keyfile_pw_cb);
- SSL_CTX_set_default_passwd_cb_userdata(impl->ctx, impl->keyfile_pw);
- }
- if (SSL_CTX_use_PrivateKey_file(impl->ctx, private_key_file,
SSL_FILETYPE_PEM) != 1) {
- _log_error("SSL_CTX_use_PrivateKey_file( %s ) failed\n",
private_key_file);
- return -4;
- }
+int pn_ssl_set_credentials( pn_ssl_t *ssl,
+ const char *certificate_file,
+ const char *private_key_file,
+ const char *password)
+{
+ if (!ssl) return 0;
- if (certificate_db) {
- impl->ca_db = true;
- if (configure_ca_database(impl->ctx, certificate_db,
listener->driver->trace)) {
- return -5;
- }
- }
- return 0;
-}
+ 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);
+ 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);
+ }
-int pn_listener_ssl_allow_unsecured_clients(pn_listener_t *listener)
-{
- if (!listener->ssl) {
- _log_error("SSL not initialized\n");
- return -1;
- }
+ if (SSL_CTX_use_PrivateKey_file(ssl->ctx, private_key_file,
SSL_FILETYPE_PEM) != 1) {
+ _log_error("SSL_CTX_use_PrivateKey_file( %s ) failed\n", private_key_file);
+ return -4;
+ }
- listener->ssl->allow_unsecured = true;
- return 0;
+ _log( ssl, "Configured local certificate file %s\n", certificate_file );
+ return 0;
}
-
-int pn_connector_ssl_client_init(pn_connector_t *connector,
- const char *certificate_db)
+int pn_ssl_set_trusted_ca_db(pn_ssl_t *ssl,
+ const char *certificate_db)
{
- if (connector->listener)
- return -1; // not for listener-based connectors
-
- connector->ssl = calloc(1, sizeof(pn_connector_ssl_t));
- if (!connector->ssl) {
- _log_error("calloc() failed: %s\n", strerror(errno));
- return -1;
- }
- pn_connector_ssl_t *impl = connector->ssl;
-
- impl->mode = SSL_CLIENT;
-
- if (!ssl_initialized) {
- ssl_initialized = 1;
- SSL_library_init();
- SSL_load_error_strings();
- }
+ if (!ssl) return 0;
- impl->ctx = SSL_CTX_new(SSLv23_client_method());
- if (!impl->ctx) {
- _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
- return -2;
- }
+ // certificates can be either a file or a directory, which determines how it
is passed
+ // to SSL_CTX_load_verify_locations()
+ struct stat sbuf;
+ if (stat( certificate_db, &sbuf ) != 0) {
+ _log_error("stat(%s) failed: %s\n", certificate_db, strerror(errno));
+ return -1;
+ }
- if (configure_ca_database(impl->ctx, certificate_db, connector->trace)) {
- return -3;
- }
+ const char *file;
+ const char *dir;
+ if (S_ISDIR(sbuf.st_mode)) {
+ dir = certificate_db;
+ file = NULL;
+ } else {
+ dir = NULL;
+ file = certificate_db;
+ }
- /* Force servers to authenticate */
- SSL_CTX_set_verify( connector->ssl->ctx,
- SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- verify_callback /*?verify callback?*/ );
- //0 /*?verify callback?*/ );
+ if (SSL_CTX_load_verify_locations( ssl->ctx, file, dir ) != 1) {
+ _log_error("SSL_CTX_load_verify_locations( %s ) failed\n", certificate_db);
+ return -1;
+ }
-#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
- SSL_CTX_set_verify_depth(connector->ssl->ctx, 1);
-#endif
+ _log( ssl, "loaded trusted CA database: file=%s dir=%s\n", file, dir );
+ return 0;
+}
- /* make the socket nonblocking*/
- int flags = fcntl(connector->fd, F_GETFL, 0);
- flags |= O_NONBLOCK;
- if (fcntl(connector->fd, F_SETFL, flags)) {
- // @todo: better error handling - fail this connection
- _log_error("fcntl() failed: %s\n", strerror(errno));
- return -1;
+int pn_ssl_allow_unsecured_client(pn_ssl_t *ssl)
+{
+ if (ssl) {
+ if (ssl->mode != SSL_MODE_SERVER) {
+ _log_error("Cannot permit unsecured clients - not a server.\n");
+ return -1;
}
-
- return start_ssl_connect( connector ); // start connecting!
+ ssl->allow_unsecured = true;
+ ssl->process_input = process_input_unknown;
+ ssl->process_output = process_output_unknown;
+ _log( ssl, "Allowing connections from unsecured clients.\n" );
+ }
+ return 0;
}
-int pn_connector_ssl_set_client_auth(pn_connector_t *connector,
- const char *certificate_file,
- const char *private_key_file,
- const char *password)
+int pn_ssl_set_peer_authentication(pn_ssl_t *ssl,
+ const pn_ssl_verify_mode_t mode,
+ const char *trusted_CAs)
{
- // @todo check state to verify not yet connected!
+ if (!ssl) return 0;
- pn_connector_ssl_t *impl = connector->ssl;
+ switch (mode) {
+ case PN_SSL_VERIFY_PEER:
- if (!impl || impl->mode != SSL_CLIENT) {
- _log_error("Error: connector not configured as SSL client.\n");
+ if (ssl->mode == 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_CTX_use_certificate_chain_file(impl->ctx, certificate_file) != 1) {
- _log_error("SSL_CTX_use_certificate_chain_file( %s ) failed\n",
certificate_file);
- return -3;
+ 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_set_client_CA_list(ssl->ssl, cert_names);
+ else {
+ _log_error("Unable to process file of trusted CAs: %s\n", trusted_CAs);
+ return -1;
+ }
}
- if (password) {
- impl->keyfile_pw = pn_strdup(password); // @todo: obfuscate me!!!
- SSL_CTX_set_default_passwd_cb(impl->ctx, keyfile_pw_cb);
- SSL_CTX_set_default_passwd_cb_userdata(impl->ctx, impl->keyfile_pw);
- }
+ SSL_CTX_set_verify( ssl->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(ssl->ctx, 1);
+#endif
+ ssl->verify_mode = PN_SSL_VERIFY_PEER;
+ break;
- if (SSL_CTX_use_PrivateKey_file(impl->ctx, private_key_file,
SSL_FILETYPE_PEM) != 1) {
- _log_error("SSL_CTX_use_PrivateKey_file( %s ) failed\n",
private_key_file);
- return -4;
- }
+ case PN_SSL_NO_VERIFY_PEER:
+ SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_NONE, NULL );
+ ssl->verify_mode = PN_SSL_NO_VERIFY_PEER;
+ break;
+ default:
+ _log_error( "Invalid peer authentication mode given.\n" );
+ return -1;
+ }
+
+ _log( ssl, "Peer authentication mode set to %s\n", (ssl->verify_mode ==
PN_SSL_VERIFY_PEER) ? "VERIFY-PEER" : "NO-VERIFY-PEER");
return 0;
}
-int pn_connector_ssl_authenticate_client(pn_connector_t *connector,
- const char *trusted_CAs_file)
+int pn_ssl_get_peer_authentication(pn_ssl_t *ssl,
+ pn_ssl_verify_mode_t *mode,
+ char *trusted_CAs, size_t *trusted_CAs_size)
{
- pn_connector_ssl_t *impl = connector->ssl;
+ if (!ssl) return -1;
- if (!impl || impl->mode != SSL_SERVER) {
- _log_error("Error: connector not configured as SSL server.\n");
+ if (mode) *mode = ssl->verify_mode;
+ if (trusted_CAs && trusted_CAs_size && *trusted_CAs_size) {
+ if (ssl->trusted_CAs) {
+ strncpy( trusted_CAs, ssl->trusted_CAs, *trusted_CAs_size );
+ trusted_CAs[*trusted_CAs_size - 1] = '\0';
+ *trusted_CAs_size = strlen(ssl->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;
+ }
+ return 0;
+}
+
+
+pn_ssl_t *pn_ssl_server(pn_transport_t *transport)
+{
+ if (!transport) return NULL;
+ if (transport->ssl) {
+ if (transport->ssl->mode != SSL_MODE_SERVER) {
+ _log_error("Error: transport already configured as a client.\n");
+ return NULL;
+ }
+ return transport->ssl;
+ }
+
+ if (!ssl_initialized) {
+ ssl_initialized = 1;
+ SSL_library_init();
+ SSL_load_error_strings();
+ }
+
+ pn_ssl_t *ssl = calloc(1, sizeof(pn_ssl_t));
+ if (!ssl) return NULL;
+
+ ssl->ctx = SSL_CTX_new(SSLv23_server_method());
+ if (!ssl->ctx) {
+ _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
+ free(ssl);
+ return NULL;
+ }
+ 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;
+ ssl->process_output = process_output_ssl;
+
+ ssl->trace = PN_TRACE_OFF;
+
+ return ssl;
+}
+
+pn_ssl_t *pn_ssl_client(pn_transport_t *transport)
+{
+ if (!transport) return NULL;
+ if (transport->ssl) {
+ if (transport->ssl->mode != SSL_MODE_CLIENT) {
+ _log_error("Error: transport already configured as a server.\n");
+ return NULL;
+ }
+ return transport->ssl;
+ }
+
+ if (!ssl_initialized) {
+ ssl_initialized = 1;
+ SSL_library_init();
+ SSL_load_error_strings();
+ }
+
+ pn_ssl_t *ssl = calloc(1, sizeof(pn_ssl_t));
+ if (!ssl) return NULL;
+
+ ssl->ctx = SSL_CTX_new(SSLv23_client_method());
+ if (!ssl->ctx) {
+ _log_error("Unable to initialize SSL context: %s\n", strerror(errno));
+ free(ssl);
+ return NULL;
+ }
+ ssl->verify_mode = PN_SSL_VERIFY_PEER;
+ SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_PEER |
SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL );
+#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
+ SSL_CTX_set_verify_depth(ssl->ctx, 1);
+#endif
- // @todo: make sure certificate_db is set...
-
-
- // load the CA's that we trust - this will be sent to the client when
client
- // authentication is requested
- STACK_OF(X509_NAME) *cert_names;
- cert_names = SSL_load_client_CA_file( trusted_CAs_file );
- if (cert_names != NULL)
- SSL_set_client_CA_list(impl->ssl, cert_names);
- else {
- _log_error("Unable to process file of trusted_CAs: %s\n",
trusted_CAs_file);
- return -1;
- }
+ 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);
- SSL_set_verify( impl->ssl,
- SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
- verify_callback /*?verify callback?*/);
- //0 /*?verify callback?*/ );
- return 0;
-}
+ // 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);
-/** Abstraction API - visible to Driver only - see ssl.h */
+ ssl->mode = SSL_MODE_CLIENT;
+ ssl->transport = transport;
+ ssl->process_input = process_input_ssl;
+ ssl->process_output = process_output_ssl;
-int pn_driver_ssl_data_ready( pn_driver_t *d )
-{
- int ready = 0;
+ ssl->trace = PN_TRACE_OFF;
- // check if any stalled readers exist, and if they have buffer space
available.
- pn_connector_t *c = d->connector_head;
- while (c) {
- if (!c->closed && c->ssl) {
- pn_connector_ssl_t *impl = c->ssl;
- if (impl->read_stalled && (c->input_size <
PN_CONNECTOR_IO_BUF_SIZE)) {
- impl->read_stalled = 0;
- c->pending_read = true;
- ready += 1;
- }
- }
- c = c->connector_next;
- }
- return ready;
+ return ssl;
}
-
-int pn_listener_init_ssl_client( pn_listener_t *l, pn_connector_t *c)
+void pn_ssl_free( pn_ssl_t *ssl)
{
- if (!l->ssl) return 0;
- assert(!c->ssl);
- c->ssl = calloc(1, sizeof(pn_connector_ssl_t));
- if (!c->ssl) {
- _log_error("calloc() failed: %s\n", strerror(errno));
- return -1;
- }
- pn_connector_ssl_t *impl = c->ssl;
-
- impl->mode = SSL_SERVER;
- impl->ctx = NULL; // share the acceptor's context
-
- /* make the socket nonblocking*/
- int flags = fcntl(c->fd, F_GETFL, 0);
-
- flags |= O_NONBLOCK;
- if (fcntl(c->fd, F_SETFL, flags)) {
- // @todo: better error handling - fail this connection
- _log_error("fcntl() failed: %s\n", strerror(errno));
- return -1;
- }
+ if (ssl->bio_ssl) BIO_free(ssl->bio_ssl);
+ if (ssl->bio_ssl_io) BIO_free(ssl->bio_ssl_io);
+ if (ssl->bio_net_io) BIO_free(ssl->bio_net_io);
+ if (ssl->ssl) SSL_free(ssl->ssl);
+ if (ssl->ctx) SSL_CTX_free(ssl->ctx);
- if (l->ssl->allow_unsecured) {
- return start_check_for_ssl(c);
- } else {
- return start_ssl_accept(c);
- }
-}
+ if (ssl->keyfile_pw) free(ssl->keyfile_pw);
+ if (ssl->trusted_CAs) free(ssl->trusted_CAs);
-void pn_connector_shutdown_ssl( pn_connector_t *c)
-{
- if (c->ssl) {
- if (start_ssl_shutdown(c) == 0)
- return; // shutting down
- }
- // if no ssl or shutdown fails, just close the underlying socket
- pn_connector_close(c);
+ free(ssl);
}
-void pn_listener_free_ssl( pn_listener_t *l )
+// move data received from the network into the SSL layer
+ssize_t pn_ssl_input(pn_ssl_t *ssl, char *bytes, size_t available)
{
- if (l->ssl) {
- pn_listener_ssl_t *impl = l->ssl;
- // note: ctx is referenced counted - will not actually free until all
child SSL
- // connections are freed.
- if (impl->ctx) SSL_CTX_free(impl->ctx);
- if (impl->keyfile_pw) {
- memset( impl->keyfile_pw, 0, strlen( impl->keyfile_pw ) );
- free( impl->keyfile_pw );
- }
- free(l->ssl);
- l->ssl = NULL;
- }
+ return ssl->process_input( ssl->transport, bytes, available );
}
-
-void pn_connector_free_ssl( pn_connector_t *c )
+// 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)
{
- if (c->ssl) {
- pn_connector_ssl_t *impl = c->ssl;
- if (impl->ssl) SSL_free(impl->ssl);
- if (impl->sbio) BIO_free(impl->sbio);
- if (impl->ctx) SSL_CTX_free(impl->ctx);
- if (impl->keyfile_pw) {
- memset( impl->keyfile_pw, 0, strlen( impl->keyfile_pw ) );
- free( impl->keyfile_pw );
- }
- free(c->ssl);
- c->ssl = NULL;
- }
+ return ssl->process_output( ssl->transport, buffer, max_size );
}
@@ -446,382 +493,325 @@ static int keyfile_pw_cb(char *buf, int
}
-// Configure the database containing trusted CA certificates
-static int configure_ca_database(SSL_CTX *ctx, const char *certificate_db,
pn_trace_t trace)
-{
- // certificates can be either a file or a directory, which determines how
it is passed
- // to SSL_CTX_load_verify_locations()
- struct stat sbuf;
- if (stat( certificate_db, &sbuf ) != 0) {
- _log_error("stat(%s) failed: %s\n", certificate_db, strerror(errno));
- return -1;
- }
- const char *file;
- const char *dir;
- if (S_ISDIR(sbuf.st_mode)) {
- dir = certificate_db;
- file = NULL;
- } else {
- dir = NULL;
- file = certificate_db;
- }
- _log(trace, "load verify locations: file=%s dir=%s\n", file, dir);
- if (SSL_CTX_load_verify_locations( ctx, file, dir ) != 1) {
- _log_error("SSL_CTX_load_verify_locations( %s ) failed\n",
certificate_db);
- return -1;
- }
- return 0;
+static int start_ssl_shutdown( pn_ssl_t *ssl )
+{
+ if (!ssl->ssl_shutdown) {
+ ssl->ssl_shutdown = true;
+ BIO_ssl_shutdown( ssl->bio_ssl );
+ }
+ return 0;
}
-
-
-/* STATE: check_for_ssl
- *
- * The server will allow both encrypted and non-encrypted clients. Determine
if
- * encryption is being used by peeking at the first few bytes of incoming data.
- */
-
-static int start_check_for_ssl( pn_connector_t *client )
+static int setup_ssl_connection( pn_ssl_t *ssl )
{
- client->status &= ~PN_SEL_WR; // don't start writing until
- client->status |= PN_SEL_RD; // we've read from the client
- client->io_handler = handle_check_for_ssl;
- return 0;
+ _log( ssl, "SSL connection detected.\n");
+ ssl->process_input = process_input_ssl;
+ ssl->process_output = process_output_ssl;
+ return 0;
}
+//////// SSL Connections
-static int handle_check_for_ssl( pn_connector_t *client )
+// 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 )
{
- unsigned char buf[5];
- int rc;
- int retries = 3;
-
- do {
- rc = recv(client->fd, buf, sizeof(buf), MSG_PEEK);
- if (rc == sizeof(buf))
- break;
- if (rc < 0) {
- if (errno == EWOULDBLOCK) {
- client->status |= PN_SEL_RD;
- return 0;
- } else if (errno != EINTR) {
- _log_error("handle_check_for_ssl() recv failed: %s\n",
strerror(errno));
- break;
+ 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\n", (int) app_bytes);
+ 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;
}
- } while (retries-- > 0);
+ }
+ }
+
+ // write outgoing data to socket
- if (rc == sizeof(buf)) {
- /*
- * SSLv2 Client Hello format
- * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
- *
- * Bytes 0-1: RECORD-LENGTH
- * Byte 2: MSG-CLIENT-HELLO (1)
- * Byte 3: CLIENT-VERSION-MSB
- * Byte 4: CLIENT-VERSION-LSB
- *
- * Allowed versions:
- * 2.0 - SSLv2
- * 3.0 - SSLv3
- * 3.1 - TLS 1.0
- * 3.2 - TLS 1.1
- * 3.3 - TLS 1.2
- *
- * The version sent in the Client-Hello is the latest version
supported by
- * the client. NSS may send version 3.x in an SSLv2 header for
- * maximum compatibility.
- */
- int isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
- ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2
(v3.1-3.3)
- (buf[3] == 2 && buf[4] == 0)); // SSL 2
-
- /*
- * SSLv3/TLS Client Hello format
- * RFC 2246
- *
- * Byte 0: ContentType (handshake - 22)
- * Bytes 1-2: ProtocolVersion {major, minor}
- *
- * Allowed versions:
- * 3.0 - SSLv3
- * 3.1 - TLS 1.0
- * 3.2 - TLS 1.1
- * 3.3 - TLS 1.2
- */
- int isSSL3Handshake = buf[0] == 22 && // handshake
- (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2
(v3.1-3.3)
-
- if (isSSL2Handshake || isSSL3Handshake) {
- _log(client->trace, "Connector %s using SSL encryption.\n",
client->name);
- return start_ssl_accept( client );
+ 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->ssl_closed) {
+ ssl->out_count = 0; // cannot write to socket, so erase app output
data
+ }
+
+ // read incoming data from 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)) {
+ _log(ssl, "Read from SSL socket failed - SSL connection closed!!\n");
+ ssl->ssl_closed = true;
+ break;
+ }
+ }
}
- _log (client->trace, "Connector %s not using SSL encryption.\n",
client->name);
- return start_clear_connected( client );
-}
+ // write incoming data to app layer
-/* STATE: ssl_connect
- *
- * Start the SSL connection to the server. This will require I/O to complete
the
- * handshake.
- */
-static int start_ssl_connect(pn_connector_t *client)
-{
- pn_connector_ssl_t *impl = client->ssl;
- if (!impl) return -1;
+ 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\n", (int) consumed);
+ 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;
+ }
+ }
+ if (!ssl->app_closed && ssl->in_count > 0 && data != ssl->inbuf)
+ memmove( ssl->inbuf, data, ssl->in_count );
+ }
- impl->ssl = SSL_new(impl->ctx);
- impl->sbio = BIO_new_socket( client->fd, BIO_NOCLOSE );
- SSL_set_bio(impl->ssl, impl->sbio, impl->sbio);
-#if 0
- // @todo reconnect support
- if (conn->reconnecting && conn->session) {
- printf("Resuming old session...\n");
- SSL_set_session(conn->ssl, conn->session);
- SSL_SESSION_free(conn->session);
- conn->session = NULL;
+ if (ssl->app_closed) {
+ ssl->in_count = 0; // cannot accept more input, drop it
}
-#endif
- return handle_ssl_connect(client);
+
+ total += activity;
+
+ } while (activity);
+
+ return total > 0 ? true : false;
}
-int handle_ssl_connect( pn_connector_t *client )
+static ssize_t process_input_ssl( pn_transport_t *transport, char *input_data,
size_t available)
{
- pn_connector_ssl_t *impl = client->ssl;
- if (!impl) return -1;
+ pn_ssl_t *ssl = transport->ssl;
+ if (!ssl) return PN_ERR;
- int rc = SSL_connect( impl->ssl );
- switch (SSL_get_error( impl->ssl, rc )) {
- case SSL_ERROR_NONE:
- // connection completed
- _log(client->trace, "SSL_connect() completed, connector=%s.\n",
client->name);
- return start_ssl_connection_up( client );
-
- case SSL_ERROR_WANT_READ:
- client->status |= PN_SEL_RD;
- break;
- case SSL_ERROR_WANT_WRITE:
- client->status |= PN_SEL_WR;
- break;
- default:
- _log_error("SSL_connect() failure: %d\n", SSL_get_error(impl->ssl,
rc));
- ERR_print_errors_fp(stderr);
- return -1;
+ ssize_t consumed = 0;
+ bool activity;
+
+ do {
+
+ activity = false;
+
+ // 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;
}
- client->io_handler = handle_ssl_connect;
- return 0;
-}
+ // process any work available at the SSL socket or application
+ if (do_socket_io(ssl))
+ activity = true;
+ } while (activity);
-/* STATE: ssl_accept
- *
- * Accept the client connection. This may require I/O to complete the
handshake.
- */
+ if (consumed == 0 && ssl->ssl_closed && ssl->app_closed)
+ return ssl->app_closed;
-static int start_ssl_accept(pn_connector_t *client)
-{
- pn_connector_ssl_t *impl = client->ssl;
- if (!impl) return -1;
- pn_listener_ssl_t *parent = client->listener->ssl;
- if (!parent) return -1;
-
- impl->sbio = BIO_new_socket(client->fd, BIO_NOCLOSE);
- impl->ssl = SSL_new(parent->ctx);
- SSL_set_bio(impl->ssl, impl->sbio, impl->sbio);
-
- return handle_ssl_accept(client);
-}
-
-static int handle_ssl_accept(pn_connector_t *client)
-{
- pn_connector_ssl_t *impl = client->ssl;
- if (!impl) return -1;
-
- int rc = SSL_accept(impl->ssl);
- switch (SSL_get_error(impl->ssl, rc)) {
- case SSL_ERROR_NONE:
- // connection completed
- _log(client->trace, "SSL_accept() completed, connector=%s.\n",
client->name);
- return start_ssl_connection_up( client );
-
- case SSL_ERROR_WANT_READ:
- client->status |= PN_SEL_RD;
- break;
- case SSL_ERROR_WANT_WRITE:
- client->status |= PN_SEL_WR;
- break;
- default:
- _log_error("SSL_accept() failure: %d\n", SSL_get_error(impl->ssl, rc));
- ERR_print_errors_fp(stderr);
- return -1;
- }
- client->io_handler = handle_ssl_accept;
- return 0;
+ _log(ssl, "Processed %d bytes from transport input.\n", (int) consumed );
+ return consumed;
}
-
-/* STATE: ssl_connection_up
- *
- * The SSL connection is up and data is passing!
- */
-int start_ssl_connection_up( pn_connector_t *c )
+static ssize_t process_output_ssl( pn_transport_t *transport, char *buffer,
size_t max_len)
{
- c->io_handler = handle_ssl_connection_up;
- c->status |= PN_SEL_RD;
- return 0;
-}
+ pn_ssl_t *ssl = transport->ssl;
+ if (!ssl) return PN_ERR;
+ ssize_t written = 0;
+ bool activity;
-int handle_ssl_connection_up( pn_connector_t *c )
-{
- int rc;
- int ssl_err;
- int read_blocked = 0;
- int write_blocked = 0;
- int need_read = 0;
- int need_write = 0;
- int input_space;
- pn_connector_ssl_t *impl = c->ssl;
- assert(impl);
-
- c->status &= ~(PN_SEL_RD | PN_SEL_WR);
-
- do {
- input_space = PN_CONNECTOR_IO_BUF_SIZE - c->input_size;
- if (!read_blocked && input_space > 0) {
- rc = SSL_read(impl->ssl, &c->input[c->input_size], input_space);
- ssl_err = SSL_get_error( impl->ssl, rc );
- if (ssl_err == SSL_ERROR_NONE) {
- c->input_size += rc;
- } else if (ssl_err == SSL_ERROR_WANT_READ) {
- // socket read blocked
- read_blocked = 1;
- need_read = 1;
- } else if (ssl_err == SSL_ERROR_WANT_WRITE) {
- read_blocked = 1;
- write_blocked = 1; // don't bother writing
- need_write = 1;
- } else {
- if (ssl_err == SSL_ERROR_ZERO_RETURN) {
- /* remote shutdown */
- _log(c->trace, "Peer has shut down the connection,
connector=%s\n", c->name);
- } else {
- _log_error("Unexpected SSL_read() failure, errno: %s,
SSL_read() status: %d\n",
- strerror(errno), ssl_err);
- }
- return start_ssl_shutdown(c);
- }
- }
+ do {
+ activity = false;
- pn_connector_process_input(c);
- pn_connector_process_output(c);
+ // process any work available at the SSL socket or application
+ if (do_socket_io(ssl))
+ activity = true;
- if (!write_blocked && c->output_size > 0) {
+ // 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);
- rc = SSL_write( impl->ssl, c->output, c->output_size );
- ssl_err = SSL_get_error( impl->ssl, rc );
- if (ssl_err == SSL_ERROR_NONE) {
- c->output_size -= rc;
- if (c->output_size > 0)
- memmove(c->output, c->output + rc, c->output_size);
- } else if (ssl_err == SSL_ERROR_WANT_WRITE) {
- /* We would have blocked */
- write_blocked = 1;
- need_write = 1;
- } else if (ssl_err == SSL_ERROR_WANT_READ) {
- read_blocked = 1;
- write_blocked = 1; // don't bother reading
- need_read = 1;
- } else {
- if (ssl_err == SSL_ERROR_ZERO_RETURN)
- _log(c->trace, "Peer has shut down the connection,
connector=%s\n", c->name);
- else {
- _log_error("Unexpected SSL_write() failure, errno: %s,
SSL_write() status: %d\n",
- strerror(errno), ssl_err);
- }
- return start_ssl_shutdown(c);
- }
- }
+ // 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);
+ }
- } while ((!read_blocked && c->input_size < PN_CONNECTOR_IO_BUF_SIZE) ||
- (!write_blocked && c->output_size));
+ 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 (need_read) c->status |= PN_SEL_RD;
- if (need_write) c->status |= PN_SEL_WR;
- // we need to re-call SSL_read when the receive buffer is drained (do not
need to wait for I/O!)
- if (!read_blocked && SSL_pending(impl->ssl) && PN_CONNECTOR_IO_BUF_SIZE ==
c->input_size) {
- impl->read_stalled = true;
- }
+//////// CLEARTEXT CONNECTIONS
- return 0;
+static ssize_t process_input_cleartext(pn_transport_t *transport, char
*input_data, size_t len)
+{
+ // just write directly to layer "above" SSL
+ return transport->process_input( transport, input_data, len );
}
-
-/* STATE: clear_connected
- *
- * The peer has connected without SSL (in the clear). Use the default I/O
handler
- */
-static int start_clear_connected( pn_connector_t *c )
+static ssize_t process_output_cleartext(pn_transport_t *transport, char
*buffer, size_t max_len)
{
- pn_connector_free_ssl( c );
- c->status |= (PN_SEL_RD | PN_SEL_WR);
- c->io_handler = pn_io_handler;
- return 0;
+ // just read directly from the layer "above" SSL
+ return transport->process_input( transport, buffer, max_len );
}
-/* STATE: ssl_shutdown
- *
- * Shutting down the SSL connection.
- */
-static int start_ssl_shutdown( pn_connector_t *c )
+
+static int setup_cleartext_connection( pn_ssl_t *ssl )
{
- if (c->closed) return 0;
- return handle_ssl_shutdown( c );
+ _log( ssl, "Cleartext connection detected.\n");
+ ssl->process_input = process_input_cleartext;
+ ssl->process_output = process_output_cleartext;
+ return 0;
}
-static int handle_ssl_shutdown( pn_connector_t *c )
+
+// until we determine if the client is using SSL or not:
+
+static ssize_t process_input_unknown(pn_transport_t *transport, char
*input_data, size_t len)
{
- int rc;
- pn_connector_ssl_t *impl = c->ssl;
- if (!impl) return -1;
-
- do {
- rc = SSL_shutdown( impl->ssl );
- } while (rc == 0);
-
- switch (SSL_get_error( impl->ssl, rc )) {
- case SSL_ERROR_WANT_READ:
- //printf(" need read...\n");
- c->status |= PN_SEL_RD;
- break;
- case SSL_ERROR_WANT_WRITE:
- //printf(" need write...\n");
- c->status |= PN_SEL_WR;
- break;
- default: // whatever- consider us closed
- case SSL_ERROR_NONE:
- _log(c->trace, "SSL connection shutdown complete code=%d,
connector=%s\n",
- SSL_get_error(impl->ssl,rc), c->name);
- // shutdown completed
- c->io_handler = pn_null_io_handler;
- pn_connector_close( c );
- return 0;
- }
- c->io_handler = handle_ssl_shutdown;
+ switch (check_for_ssl_connection( input_data, len )) {
+ case SSL_CONNECTION:
+ setup_ssl_connection( transport->ssl );
+ return transport->ssl->process_input( transport, input_data, len );
+ case CLEAR_CONNECTION:
+ setup_cleartext_connection( transport->ssl );
+ return transport->ssl->process_input( transport, input_data, len );
+ default:
return 0;
+ }
}
+static ssize_t process_output_unknown(pn_transport_t *transport, char
*input_data, size_t len)
+{
+ // do not do output until we know if SSL is used or not
+ return 0;
+}
+
+static connection_mode_t check_for_ssl_connection( const char *data, size_t
len )
+{
+ if (len >= 5) {
+ const unsigned char *buf = (unsigned char *)data;
+ /*
+ * SSLv2 Client Hello format
+ * http://www.mozilla.org/projects/security/pki/nss/ssl/draft02.html
+ *
+ * Bytes 0-1: RECORD-LENGTH
+ * Byte 2: MSG-CLIENT-HELLO (1)
+ * Byte 3: CLIENT-VERSION-MSB
+ * Byte 4: CLIENT-VERSION-LSB
+ *
+ * Allowed versions:
+ * 2.0 - SSLv2
+ * 3.0 - SSLv3
+ * 3.1 - TLS 1.0
+ * 3.2 - TLS 1.1
+ * 3.3 - TLS 1.2
+ *
+ * The version sent in the Client-Hello is the latest version supported by
+ * the client. NSS may send version 3.x in an SSLv2 header for
+ * maximum compatibility.
+ */
+ int isSSL2Handshake = buf[2] == 1 && // MSG-CLIENT-HELLO
+ ((buf[3] == 3 && buf[4] <= 3) || // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
+ (buf[3] == 2 && buf[4] == 0)); // SSL 2
+ /*
+ * SSLv3/TLS Client Hello format
+ * RFC 2246
+ *
+ * Byte 0: ContentType (handshake - 22)
+ * Bytes 1-2: ProtocolVersion {major, minor}
+ *
+ * Allowed versions:
+ * 3.0 - SSLv3
+ * 3.1 - TLS 1.0
+ * 3.2 - TLS 1.1
+ * 3.3 - TLS 1.2
+ */
+ int isSSL3Handshake = buf[0] == 22 && // handshake
+ (buf[1] == 3 && buf[2] <= 3); // SSL 3.0 & TLS 1.0-1.2 (v3.1-3.3)
+ if (isSSL2Handshake || isSSL3Handshake) {
+ return SSL_CONNECTION;
+ } else {
+ return CLEAR_CONNECTION;
+ }
+ }
+ return UNKNOWN_CONNECTION;
+}
+void pn_ssl_trace(pn_ssl_t *ssl, pn_trace_t trace)
+{
+ ssl->trace = trace;
+}
Modified:
qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl-internal.h
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl-internal.h?rev=1387131&r1=1387130&r2=1387131&view=diff
==============================================================================
--- qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl-internal.h
(original)
+++ qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl-internal.h Tue
Sep 18 13:08:56 2012
@@ -1,5 +1,5 @@
-#ifndef PROTON_SRC_DRIVERS_SSL_H
-#define PROTON_SRC_DRIVERS_SSL_H 1
+#ifndef PROTON_SSL_INTERNAL_H
+#define PROTON_SSL_INTERNAL_H 1
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
@@ -80,5 +80,15 @@ void pn_ssl_free( pn_ssl_t *ssl);
*/
int pn_driver_ssl_data_ready( pn_driver_t *d );
-void pn_connector_shutdown_ssl(pn_connector_t *c); // fuck you
-#endif /* ssl.h */
+void pn_connector_shutdown_ssl(pn_connector_t *c); // @todo: can I remove
this???
+
+// move data received from the network into the SSL layer
+ssize_t pn_ssl_input(pn_ssl_t *ssl, char *bytes, size_t available);
+
+// copy data generated by SSL into the network's output buffers
+ssize_t pn_ssl_output(pn_ssl_t *ssl, char *buffer, size_t max_size);
+
+
+void pn_ssl_trace(pn_ssl_t *ssl, pn_trace_t trace);
+
+#endif /* ssl-internal.h */
Modified: qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl_stub.c
URL:
http://svn.apache.org/viewvc/qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl_stub.c?rev=1387131&r1=1387130&r2=1387131&view=diff
==============================================================================
--- qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl_stub.c
(original)
+++ qpid/proton/branches/driver_abstraction/proton-c/src/ssl/ssl_stub.c Tue Sep
18 13:08:56 2012
@@ -88,5 +88,12 @@ void pn_connector_shutdown_ssl(pn_connec
pn_connector_close(c);
}
+void pn_ssl_free( pn_ssl_t *ssl)
+{
+}
+
+void pn_ssl_trace(pn_ssl_t *ssl, pn_trace_t trace)
+{
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]