Author: kgiusti
Date: Tue Oct 9 22:20:08 2012
New Revision: 1396381
URL: http://svn.apache.org/viewvc?rev=1396381&view=rev
Log:
PROTON-64: added verification modes to allow anonymous ciphers.
Modified:
qpid/proton/trunk/proton-c/bindings/python/proton.py
qpid/proton/trunk/proton-c/include/proton/ssl.h
qpid/proton/trunk/proton-c/src/ssl/openssl.c
qpid/proton/trunk/tests/proton_tests/ssl.py
Modified: qpid/proton/trunk/proton-c/bindings/python/proton.py
URL:
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/bindings/python/proton.py?rev=1396381&r1=1396380&r2=1396381&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/python/proton.py (original)
+++ qpid/proton/trunk/proton-c/bindings/python/proton.py Tue Oct 9 22:20:08
2012
@@ -1692,6 +1692,7 @@ class SSL(object):
VERIFY_PEER = PN_SSL_VERIFY_PEER
NO_VERIFY_PEER = PN_SSL_NO_VERIFY_PEER
+ ANONYMOUS_PEER = PN_SSL_ANONYMOUS_PEER
def set_peer_authentication(self, verify_mode, trusted_CAs=None):
return self._check( pn_ssl_set_peer_authentication(self._ssl, verify_mode,
Modified: qpid/proton/trunk/proton-c/include/proton/ssl.h
URL:
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/include/proton/ssl.h?rev=1396381&r1=1396380&r2=1396381&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/include/proton/ssl.h (original)
+++ qpid/proton/trunk/proton-c/include/proton/ssl.h Tue Oct 9 22:20:08 2012
@@ -128,10 +128,25 @@ int pn_ssl_set_trusted_ca_db(pn_ssl_t *s
int pn_ssl_allow_unsecured_client(pn_ssl_t *ssl);
-/** Determines the level of peer certificate validation. */
+/** Determines the level of peer validation.
+ *
+ * VERIFY_PEER will only connect to those peers that provide a valid
identifying
+ * certificate signed by a trusted CA and are using an authenticated cipher.
+ * NO_VERIFY_PEER does not require the peer to provide a certificate, but
does require
+ * use of an authenticated ciper. ANONYMOUS_PEER not only does not require a
valid
+ * certificate, but it permits use of ciphers that do not provide
authentication.
+ *
+ * By default, a client will use VERIFY_PEER.
+ *
+ * A server will initially use ANONYMOUS_PEER until a certificate is
configured via
+ * ::pn_ssl_set_credentials(), then the server will use NO_VERIFY_PEER.
+ *
+ * These default settings can be changed via
::pn_ssl_set_peer_authentication()
+ */
typedef enum {
PN_SSL_VERIFY_PEER, /**< require peer to provide a valid identifying
certificate */
- PN_SSL_NO_VERIFY_PEER /**< do not require peer to provide an identifying
certificate */
+ PN_SSL_NO_VERIFY_PEER, /**< do not require peer to provide an identifying
certificate */
+ PN_SSL_ANONYMOUS_PEER, /**< do not require a certificate nor cipher
authorization */
} pn_ssl_verify_mode_t;
Modified: qpid/proton/trunk/proton-c/src/ssl/openssl.c
URL:
http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/src/ssl/openssl.c?rev=1396381&r1=1396380&r2=1396381&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/src/ssl/openssl.c (original)
+++ qpid/proton/trunk/proton-c/src/ssl/openssl.c Tue Oct 9 22:20:08 2012
@@ -28,6 +28,7 @@
#include "../util.h"
#include <openssl/ssl.h>
+#include <openssl/dh.h>
#include <openssl/err.h>
#include <sys/types.h>
#include <sys/stat.h>
@@ -49,10 +50,11 @@ struct pn_ssl_t {
SSL_CTX *ctx;
SSL *ssl;
pn_ssl_mode_t mode;
- bool allow_unsecured;
- bool ca_db; // true when CA database configured
+ 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; // NEED INIT
+ pn_ssl_verify_mode_t verify_mode;
char *trusted_CAs;
pn_transport_t *transport;
@@ -82,6 +84,10 @@ struct pn_ssl_t {
pn_trace_t trace;
};
+// define two sets of allowable ciphers: those that require authentication,
and those
+// that do not require authentication (anonymous). See ciphers(1).
+#define CIPHERS_AUTHENTICATE "ALL:!aNULL:!eNULL:@STRENGTH"
+#define CIPHERS_ANONYMOUS "ALL:+aNULL:!eNULL:@STRENGTH"
/* */
static int keyfile_pw_cb(char *buf, int size, int rwflag, void *userdata);
@@ -115,9 +121,18 @@ static void _log(pn_ssl_t *ssl, const ch
}
}
-static void _log_ssl_error(pn_ssl_t *ssl)
+// log an error and dump the SSL error stack
+static void _log_ssl_error(pn_ssl_t *ssl, const char *fmt, ...)
{
char buf[128]; // see "man ERR_error_string_n()"
+ va_list ap;
+
+ if (fmt) {
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+
unsigned long err = ERR_get_error();
while (err) {
ERR_error_string_n(err, buf, sizeof(buf));
@@ -186,6 +201,46 @@ static int verify_callback(int preverify
#endif
+// this code was generated using the command:
+// "openssl dhparam -C -2 2048"
+static DH *get_dh2048()
+{
+ static unsigned char dh2048_p[]={
+ 0xAE,0xF7,0xE9,0x66,0x26,0x7A,0xAC,0x0A,0x6F,0x1E,0xCD,0x81,
+ 0xBD,0x0A,0x10,0x7E,0xFA,0x2C,0xF5,0x2D,0x98,0xD4,0xE7,0xD9,
+ 0xE4,0x04,0x8B,0x06,0x85,0xF2,0x0B,0xA3,0x90,0x15,0x56,0x0C,
+ 0x8B,0xBE,0xF8,0x48,0xBB,0x29,0x63,0x75,0x12,0x48,0x9D,0x7E,
+ 0x7C,0x24,0xB4,0x3A,0x38,0x7E,0x97,0x3C,0x77,0x95,0xB0,0xA2,
+ 0x72,0xB6,0xE9,0xD8,0xB8,0xFA,0x09,0x1B,0xDC,0xB3,0x80,0x6E,
+ 0x32,0x0A,0xDA,0xBB,0xE8,0x43,0x88,0x5B,0xAB,0xC3,0xB2,0x44,
+ 0xE1,0x95,0x85,0x0A,0x0D,0x13,0xE2,0x02,0x1E,0x96,0x44,0xCF,
+ 0xA0,0xD8,0x46,0x32,0x68,0x63,0x7F,0x68,0xB3,0x37,0x52,0xCE,
+ 0x3A,0x4E,0x48,0x08,0x7F,0xD5,0x53,0x00,0x59,0xA8,0x2C,0xCB,
+ 0x51,0x64,0x3D,0x5F,0xEF,0x0E,0x5F,0xE6,0xAF,0xD9,0x1E,0xA2,
+ 0x35,0x64,0x37,0xD7,0x4C,0xC9,0x24,0xFD,0x2F,0x75,0xBB,0x3A,
+ 0x15,0x82,0x76,0x4D,0xC2,0x8B,0x1E,0xB9,0x4B,0xA1,0x33,0xCF,
+ 0xAA,0x3B,0x7C,0xC2,0x50,0x60,0x6F,0x45,0x69,0xD3,0x6B,0x88,
+ 0x34,0x9B,0xE4,0xF8,0xC6,0xC7,0x5F,0x10,0xA1,0xBA,0x01,0x8C,
+ 0xDA,0xD1,0xA3,0x59,0x9C,0x97,0xEA,0xC3,0xF6,0x02,0x55,0x5C,
+ 0x92,0x1A,0x39,0x67,0x17,0xE2,0x9B,0x27,0x8D,0xE8,0x5C,0xE9,
+ 0xA5,0x94,0xBB,0x7E,0x16,0x6F,0x53,0x5A,0x6D,0xD8,0x03,0xC2,
+ 0xAC,0x7A,0xCD,0x22,0x98,0x8E,0x33,0x2A,0xDE,0xAB,0x12,0xC0,
+ 0x0B,0x7C,0x0C,0x20,0x70,0xD9,0x0B,0xAE,0x0B,0x2F,0x20,0x9B,
+ 0xA4,0xED,0xFD,0x49,0x0B,0xE3,0x4A,0xF6,0x28,0xB3,0x98,0xB0,
+ 0x23,0x1C,0x09,0x33,
+ };
+ static unsigned char dh2048_g[]={
+ 0x02,
+ };
+ DH *dh;
+
+ if ((dh=DH_new()) == NULL) return(NULL);
+ dh->p=BN_bin2bn(dh2048_p,sizeof(dh2048_p),NULL);
+ dh->g=BN_bin2bn(dh2048_g,sizeof(dh2048_g),NULL);
+ if ((dh->p == NULL) || (dh->g == NULL))
+ { DH_free(dh); return(NULL); }
+ return(dh);
+}
/** Public API - visible to application code */
@@ -203,8 +258,7 @@ int pn_ssl_set_credentials( pn_ssl_t *ss
}
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);
- _log_ssl_error(ssl);
+ _log_ssl_error(ssl, "SSL_CTX_use_certificate_chain_file( %s ) failed\n",
certificate_file);
return -3;
}
@@ -215,18 +269,26 @@ int pn_ssl_set_credentials( pn_ssl_t *ss
}
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);
- _log_ssl_error(ssl);
+ _log_ssl_error(ssl, "SSL_CTX_use_PrivateKey_file( %s ) failed\n",
private_key_file);
return -4;
}
if (SSL_CTX_check_private_key(ssl->ctx) != 1) {
- _log_error("The key file %s is not consistent with the certificate %s\n",
- private_key_file, certificate_file);
- _log_ssl_error(ssl);
+ _log_ssl_error(ssl, "The key file %s is not consistent with the
certificate %s\n",
+ private_key_file, certificate_file);
return -5;
}
+ ssl->has_certificate = true;
+
+ // Now that a certificate is configured, we can eliminate those ciphers that
do not
+ // authenticate as they are vulnerable to Man in the Middle attacks.
+ if (ssl->verify_mode == PN_SSL_ANONYMOUS_PEER) {
+ if (pn_ssl_set_peer_authentication(ssl, PN_SSL_NO_VERIFY_PEER, NULL)) {
+ return -2;
+ }
+ }
+
_log( ssl, "Configured local certificate file %s\n", certificate_file );
return 0;
}
@@ -260,10 +322,12 @@ int pn_ssl_set_trusted_ca_db(pn_ssl_t *s
}
if (SSL_CTX_load_verify_locations( ssl->ctx, file, dir ) != 1) {
- _log_error("SSL_CTX_load_verify_locations( %s ) failed\n", certificate_db);
+ _log_ssl_error(ssl, "SSL_CTX_load_verify_locations( %s ) failed\n",
certificate_db);
return -1;
}
+ ssl->has_ca_db = true;
+
_log( ssl, "loaded trusted CA database: file=%s dir=%s\n", file, dir );
return 0;
}
@@ -322,12 +386,36 @@ int pn_ssl_set_peer_authentication(pn_ss
#if (OPENSSL_VERSION_NUMBER < 0x00905100L)
SSL_CTX_set_verify_depth(ssl->ctx, 1);
#endif
- ssl->verify_mode = PN_SSL_VERIFY_PEER;
+
+ // Since we require the peer to authenticate, we should avoid anonymous
ciphers (KAG:
+ // is this a reasonable approach?)
+ if (!SSL_CTX_set_cipher_list( ssl->ctx, CIPHERS_AUTHENTICATE )) {
+ _log_ssl_error(ssl, "Failed to set cipher list to %s\n",
CIPHERS_AUTHENTICATE);
+ return -2;
+ }
+ _log( ssl, "Peer authentication mode set to VERIFY-PEER\n");
break;
case PN_SSL_NO_VERIFY_PEER:
SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_NONE, NULL );
- ssl->verify_mode = PN_SSL_NO_VERIFY_PEER;
+
+ // Still require authenticating ciphers, since they are stronger than
anonymous (KAG:
+ // again - ok?)
+ if (!SSL_CTX_set_cipher_list( ssl->ctx, CIPHERS_AUTHENTICATE )) {
+ _log_ssl_error(ssl, "Failed to set cipher list to %s\n",
CIPHERS_AUTHENTICATE);
+ return -2;
+ }
+ _log( ssl, "Peer authentication mode set to NO-VERIFY-PEER\n");
+ break;
+
+ case PN_SSL_ANONYMOUS_PEER: // hippie free love mode... :)
+ SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_NONE, NULL );
+
+ 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;
+ }
+ _log( ssl, "Peer authentication mode set to ANONYMOUS-PEER\n");
break;
default:
@@ -335,8 +423,8 @@ int pn_ssl_set_peer_authentication(pn_ss
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;
+ ssl->verify_mode = mode;
+ return 0;
}
@@ -378,32 +466,44 @@ int pn_ssl_init(pn_ssl_t *ssl, pn_ssl_mo
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) {
_log_error("Unable to initialize SSL context: %s\n", strerror(errno));
return -1;
}
- // default: always verify the remote server
- 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
+
+ // default: always verify the remote server, and only use ciphers that
authenticate.
+ // This can be changed by pn_ssl_set_peer_authentication().
+ if (pn_ssl_set_peer_authentication( ssl, PN_SSL_VERIFY_PEER, NULL)) {
+ return -2;
+ }
break;
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) {
_log_error("Unable to initialize SSL context: %s\n", strerror(errno));
return -1;
}
- // default: no client authentication
- ssl->verify_mode = PN_SSL_NO_VERIFY_PEER;
- SSL_CTX_set_verify( ssl->ctx, SSL_VERIFY_NONE, NULL );
- ssl->mode = PN_SSL_MODE_SERVER;
+
+ // default: no client authentication, allow ciphers that do not support
+ // authentication (until a certificate is configured).
+ if (pn_ssl_set_peer_authentication( ssl, PN_SSL_ANONYMOUS_PEER, NULL )) {
+ return -2;
+ }
break;
}
+
+ 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;
}
@@ -550,7 +650,7 @@ static ssize_t process_input_ssl( pn_tra
} else {
if (!BIO_should_retry(ssl->bio_ssl)) {
_log(ssl, "Read from SSL socket failed - SSL connection closed!!\n");
- _log_ssl_error(ssl);
+ _log_ssl_error(ssl, NULL);
start_ssl_shutdown(ssl); // KAG: not sure - this may be
necessary
ssl->ssl_closed = true;
} else {
@@ -668,7 +768,7 @@ static ssize_t process_output_ssl( pn_tr
} else {
if (!BIO_should_retry(ssl->bio_ssl)) {
_log(ssl, "Write to SSL socket failed - SSL connection
closed!!\n");
- _log_ssl_error(ssl);
+ _log_ssl_error(ssl, NULL);
start_ssl_shutdown(ssl); // KAG: not sure - this may be
necessary
ssl->out_count = 0; // can no longer write to socket, so
erase app output data
ssl->ssl_closed = true;
Modified: qpid/proton/trunk/tests/proton_tests/ssl.py
URL:
http://svn.apache.org/viewvc/qpid/proton/trunk/tests/proton_tests/ssl.py?rev=1396381&r1=1396380&r2=1396381&view=diff
==============================================================================
--- qpid/proton/trunk/tests/proton_tests/ssl.py (original)
+++ qpid/proton/trunk/tests/proton_tests/ssl.py Tue Oct 9 22:20:08 2012
@@ -72,6 +72,21 @@ class SslTest(common.Test):
server_conn.close()
self._pump()
+ def test_server_anonymous(self):
+ """ Simple SSL connection without configuring a server certificate, and
+ configuring the client to accept an unverified peer.
+ """
+ self.client.set_peer_authentication( SSL.ANONYMOUS_PEER )
+ client_conn = Connection()
+ self.t_client.bind(client_conn)
+ server_conn = Connection()
+ self.t_server.bind(server_conn)
+ client_conn.open()
+ server_conn.open()
+ self._pump()
+ client_conn.close()
+ server_conn.close()
+ self._pump()
def test_client_authentication(self):
""" @TODO: fix
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]