You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ac...@apache.org on 2015/03/11 17:19:01 UTC

svn commit: r1665930 - in /qpid/dispatch/branches/0.4: src/server.c tests/system_tests_qdstat.py

Author: aconway
Date: Wed Mar 11 16:19:00 2015
New Revision: 1665930

URL: http://svn.apache.org/r1665930
Log:
DISPATCH-124: Error handling when SSL is not available.

Fix tests and error handling.

Modified:
    qpid/dispatch/branches/0.4/src/server.c
    qpid/dispatch/branches/0.4/tests/system_tests_qdstat.py

Modified: qpid/dispatch/branches/0.4/src/server.c
URL: http://svn.apache.org/viewvc/qpid/dispatch/branches/0.4/src/server.c?rev=1665930&r1=1665929&r2=1665930&view=diff
==============================================================================
--- qpid/dispatch/branches/0.4/src/server.c (original)
+++ qpid/dispatch/branches/0.4/src/server.c Wed Mar 11 16:19:00 2015
@@ -101,7 +101,66 @@ qd_error_t qd_entity_refresh_connection(
     return qd_error_code();
 }
 
+static qd_error_t listener_setup_ssl(const qd_server_config_t *config, pn_transport_t *tport) {
 
+    pn_ssl_domain_t *domain = pn_ssl_domain(PN_SSL_MODE_SERVER);
+    if (!domain) return qd_error(QD_ERROR_RUNTIME, "No SSL support");
+
+    // setup my identifying cert:
+    if (pn_ssl_domain_set_credentials(domain,
+				      config->ssl_certificate_file,
+				      config->ssl_private_key_file,
+				      config->ssl_password)) {
+	pn_ssl_domain_free(domain);
+	return qd_error(QD_ERROR_RUNTIME, "Cannot set SSL credentials");
+    }
+    if (config->ssl_allow_unsecured_client) {
+	if (pn_ssl_domain_allow_unsecured_client(domain)) {
+	    pn_ssl_domain_free(domain);
+	    return qd_error(QD_ERROR_RUNTIME, "Cannot allow unsecured client");
+	}
+    }
+
+    // for peer authentication:
+    if (config->ssl_trusted_certificate_db) {
+	if (pn_ssl_domain_set_trusted_ca_db(domain, config->ssl_trusted_certificate_db)) {
+	    pn_ssl_domain_free(domain);
+	    return qd_error(QD_ERROR_RUNTIME, "Cannot set truested SSL CA" );
+	}
+    }
+
+    const char *trusted = config->ssl_trusted_certificate_db;
+    if (config->ssl_trusted_certificates)
+	trusted = config->ssl_trusted_certificates;
+
+    // do we force the peer to send a cert?
+    if (config->ssl_require_peer_authentication) {
+	if (!trusted || pn_ssl_domain_set_peer_authentication(domain, PN_SSL_VERIFY_PEER, trusted)) {
+	    pn_ssl_domain_free(domain);
+	    return qd_error(QD_ERROR_RUNTIME, "Cannot set peer authentication");
+	}
+    }
+
+    pn_ssl_t *ssl = pn_ssl(tport);
+    if (!ssl || pn_ssl_init(ssl, domain, 0)) {
+	pn_ssl_domain_free(domain);
+	return qd_error(QD_ERROR_RUNTIME, "Cannot initialize SSL");
+    }
+
+    return QD_ERROR_NONE;
+}
+
+// Format the identity of an incoming connection to buf for logging
+static const char *log_incoming(char *buf, size_t size, qdpn_connector_t *cxtr)
+{
+    qd_listener_t *qd_listener = qdpn_listener_context(qdpn_connector_listener(cxtr));
+    assert(qd_listener);
+    const char *cname = qdpn_connector_name(cxtr);
+    const char *host = qd_listener->config->host;
+    const char *port = qd_listener->config->port;
+    snprintf(buf, size, "incoming connection from %s to %s:%s", cname, host, port);
+    return buf;
+}
 
 static void thread_process_listeners(qd_server_t *qd_server)
 {
@@ -115,17 +174,10 @@ static void thread_process_listeners(qd_
         if (!cxtr)
             continue;
 
-        // Information for error messages
-        qd_listener_t *qd_listener = qdpn_listener_context(listener);
-        assert(qd_listener);
-        const char *cname = qdpn_connector_name(cxtr);
-        const char *host = qd_listener->config->host;
-        const char *port = qd_listener->config->port;
-        assert(cname && host && port);
-
-#define FROM_TO " connection from %s to %s:%s", cname, host, port
+	char logbuf[qd_log_max_len()];
 
-        qd_log(qd_server->log_source, QD_LOG_DEBUG, "Accepting" FROM_TO);
+        qd_log(qd_server->log_source, QD_LOG_DEBUG, "Accepting %s",
+	       log_incoming(logbuf, sizeof(logbuf), cxtr));
         ctx = new_qd_connection_t();
         DEQ_ITEM_INIT(ctx);
         ctx->state        = CONN_STATE_OPENING;
@@ -133,7 +185,7 @@ static void thread_process_listeners(qd_
         ctx->enqueued     = 0;
         ctx->pn_cxtr      = cxtr;
         ctx->collector    = 0;
-        ctx->listener     = qd_listener;
+        ctx->listener     = qdpn_listener_context(listener);
         ctx->connector    = 0;
         ctx->context      = ctx->listener->context;
         ctx->user_context = 0;
@@ -151,6 +203,7 @@ static void thread_process_listeners(qd_
         qdpn_connector_set_connection(cxtr, conn);
         pn_connection_set_context(conn, ctx);
         ctx->pn_conn = conn;
+        qdpn_connector_set_context(cxtr, ctx);
 
         // qd_server->lock is already locked
         DEQ_INSERT_TAIL(qd_server->connections, ctx);
@@ -168,60 +221,15 @@ static void thread_process_listeners(qd_
         pn_transport_set_server(tport);
         pn_transport_set_max_frame(tport, config->max_frame_size);
 
-        //
-        // Set up SSL if appropriate
-        //
+        // Set up SSL if configured
         if (config->ssl_enabled) {
-
-            pn_ssl_domain_t *domain = pn_ssl_domain(PN_SSL_MODE_SERVER);
-            if (!domain) {
-                qd_log(qd_server->log_source, QD_LOG_ERROR, "SSL setup failed on" FROM_TO);
-                continue;
-            }
-
-#define ERROR(MSG) do {                                                 \
-                qd_log(qd_server->log_source, QD_LOG_ERROR, MSG FROM_TO); \
-                goto ssl_error;                                         \
-            } while(0)
-
-            // setup my identifying cert:
-            if (pn_ssl_domain_set_credentials(domain,
-                                              config->ssl_certificate_file,
-                                              config->ssl_private_key_file,
-                                              config->ssl_password)) {
-                ERROR("SSL credentials failed on");
-            }
-            if (config->ssl_allow_unsecured_client) {
-                if (pn_ssl_domain_allow_unsecured_client(domain)) {
-                    ERROR("SSL cannot allow unsecured client on");
-                }
+	    qd_log(qd_server->log_source, QD_LOG_TRACE, "Configuring SSL on %s",
+		   log_incoming(logbuf, sizeof(logbuf), cxtr));
+            if (listener_setup_ssl(config, tport) != QD_ERROR_NONE) {
+                qd_log(qd_server->log_source, QD_LOG_ERROR, "%s on %s",
+                       qd_error_message(), log_incoming(logbuf, sizeof(logbuf), cxtr));
+                qdpn_connector_close(cxtr);
             }
-
-            // for peer authentication:
-            if (config->ssl_trusted_certificate_db) {
-                if (pn_ssl_domain_set_trusted_ca_db(domain, config->ssl_trusted_certificate_db)) {
-                    ERROR("SSL CA configuration failed on" );
-                }
-            }
-
-            const char *trusted = config->ssl_trusted_certificate_db;
-            if (config->ssl_trusted_certificates)
-                trusted = config->ssl_trusted_certificates;
-
-            // do we force the peer to send a cert?
-            if (config->ssl_require_peer_authentication) {
-                if (pn_ssl_domain_set_peer_authentication(domain, PN_SSL_VERIFY_PEER, trusted)) {
-                    ERROR("SSL Authentication configuration failed on");
-                }
-            }
-            pn_ssl_t *ssl = pn_ssl(tport);
-            if (!ssl || pn_ssl_init(ssl, domain, 0)) {
-                ERROR("SSL setup failed");
-            }
-            qd_log(qd_server->log_source, QD_LOG_TRACE, "Configured SSL on" FROM_TO);
-
-        ssl_error:
-            pn_ssl_domain_free(domain);
         }
 
         //
@@ -233,7 +241,6 @@ static void thread_process_listeners(qd_
         pn_sasl_allow_skip(sasl, config->allow_no_sasl);
         pn_sasl_done(sasl, PN_SASL_OK);  // TODO - This needs to go away
 
-        qdpn_connector_set_context(cxtr, ctx);
         ctx->owner_thread = CONTEXT_NO_OWNER;
     }
 }

Modified: qpid/dispatch/branches/0.4/tests/system_tests_qdstat.py
URL: http://svn.apache.org/viewvc/qpid/dispatch/branches/0.4/tests/system_tests_qdstat.py?rev=1665930&r1=1665929&r2=1665930&view=diff
==============================================================================
--- qpid/dispatch/branches/0.4/tests/system_tests_qdstat.py (original)
+++ qpid/dispatch/branches/0.4/tests/system_tests_qdstat.py Wed Mar 11 16:19:00 2015
@@ -22,44 +22,15 @@ import re
 import system_test
 import unittest
 from subprocess import PIPE
-from proton import Url
+from proton import Url, SSLDomain, SSLUnavailable
 
 class QdstatTest(system_test.TestCase):
     """Test qdstat tool output"""
-
-    @staticmethod
-    def ssl_file(name):
-        return os.path.join(os.path.dirname(__file__), 'ssl_certs', name)
-
     @classmethod
     def setUpClass(cls):
         super(QdstatTest, cls).setUpClass()
         config = system_test.Qdrouterd.Config([
-            ('ssl-profile', {'name': 'server-ssl-strict',
-                             'cert-db': cls.ssl_file('ca-certificate.pem'),
-                             'cert-file': cls.ssl_file('server-certificate.pem'),
-                             'key-file': cls.ssl_file('server-private-key.pem'),
-                             'password': 'server-password',
-                             'allow-unsecured': False,
-                             'require-peer-auth': False}),
-            ('ssl-profile', {'name': 'server-ssl-unsecured',
-                             'cert-db': cls.ssl_file('ca-certificate.pem'),
-                             'cert-file': cls.ssl_file('server-certificate.pem'),
-                             'key-file': cls.ssl_file('server-private-key.pem'),
-                             'password': 'server-password',
-                             'allow-unsecured': True,
-                             'require-peer-auth': False}),
-            ('ssl-profile', {'name': 'server-ssl-auth',
-                             'cert-db': cls.ssl_file('ca-certificate.pem'),
-                             'cert-file': cls.ssl_file('server-certificate.pem'),
-                             'key-file': cls.ssl_file('server-private-key.pem'),
-                             'password': 'server-password',
-                             'allow-unsecured': False,
-                             'require-peer-auth': True}),
             ('listener', {'port': cls.tester.get_port()}),
-            ('listener', {'port': cls.tester.get_port(), 'ssl-profile': 'server-ssl-strict'}),
-            ('listener', {'port': cls.tester.get_port(), 'ssl-profile': 'server-ssl-unsecured'}),
-            ('listener', {'port': cls.tester.get_port(), 'ssl-profile': 'server-ssl-auth'})
         ])
         cls.router = cls.tester.qdrouterd('test-router', config)
 
@@ -94,57 +65,159 @@ class QdstatTest(system_test.TestCase):
         self.run_qdstat(['--memory'], r'qd_address_t\s+[0-9]+')
 
     def do_test(self, url, args):
-        # FIXME aconway 2015-02-18: shouldn't be timing out, should be rejected.
-        self.run_qdstat(['--general', '--timeout=0.5'] + args, 
+        self.run_qdstat(['--general'] + args, 
                         regexp=r'(?s)Router Statistics.*Mode\s*Standalone',
                         address=str(url))
 
-    def test_ssl(self):
-        """
-        Test the matrix of dispatch and client SSL configuratoin and ensure we 
-        can/can't connect as expected.
-        """
-        trustfile = ['--ssl-trustfile', self.ssl_file('ca-certificate.pem')]
-        bad_trustfile = ['--ssl-trustfile', self.ssl_file('bad-ca-certificate.pem')]
-        client_cert = ['--ssl-certificate', self.ssl_file('client-certificate.pem')]
-        client_key = ['--ssl-key', self.ssl_file('client-private-key.pem')]
-        client_pass = ['--ssl-password', 'client-password']
-        client_cert_all = client_cert + client_key + client_pass
-
-        addrs = [self.router.addresses[i] for i in xrange(4)];
-        none, strict, unsecured, auth = addrs
-        none_s, strict_s, unsecured_s, auth_s = (Url(a, scheme="amqps") for a in addrs)
+try:
+    SSLDomain(SSLDomain.MODE_CLIENT)
+    class QdstatSslTest(system_test.TestCase):
+        """Test qdstat tool output"""
+
+        @staticmethod
+        def ssl_file(name):
+            return os.path.join(os.path.dirname(__file__), 'ssl_certs', name)
+
+        @classmethod
+        def setUpClass(cls):
+            super(QdstatSslTest, cls).setUpClass()
+            config = system_test.Qdrouterd.Config([
+                ('ssl-profile', {'name': 'server-ssl-strict',
+                                 'cert-db': cls.ssl_file('ca-certificate.pem'),
+                                 'cert-file': cls.ssl_file('server-certificate.pem'),
+                                 'key-file': cls.ssl_file('server-private-key.pem'),
+                                 'password': 'server-password',
+                                 'allow-unsecured': False,
+                                 'require-peer-auth': False}),
+                ('ssl-profile', {'name': 'server-ssl-unsecured',
+                                 'cert-db': cls.ssl_file('ca-certificate.pem'),
+                                 'cert-file': cls.ssl_file('server-certificate.pem'),
+                                 'key-file': cls.ssl_file('server-private-key.pem'),
+                                 'password': 'server-password',
+                                 'allow-unsecured': True,
+                                 'require-peer-auth': False}),
+                ('ssl-profile', {'name': 'server-ssl-auth',
+                                 'cert-db': cls.ssl_file('ca-certificate.pem'),
+                                 'cert-file': cls.ssl_file('server-certificate.pem'),
+                                 'key-file': cls.ssl_file('server-private-key.pem'),
+                                 'password': 'server-password',
+                                 'allow-unsecured': False,
+                                 'require-peer-auth': True}),
+                ('listener', {'port': cls.tester.get_port()}),
+                ('listener', {'port': cls.tester.get_port(), 'ssl-profile': 'server-ssl-strict'}),
+                ('listener', {'port': cls.tester.get_port(), 'ssl-profile': 'server-ssl-unsecured'}),
+                ('listener', {'port': cls.tester.get_port(), 'ssl-profile': 'server-ssl-auth'})
+            ])
+            cls.router = cls.tester.qdrouterd('test-router', config)
+
+        def run_qdstat(self, args, regexp=None, address=None):
+            p = self.popen(
+                ['qdstat', '--bus', str(address or self.router.addresses[0])] + args,
+                name='qdstat-'+self.id(), stdout=PIPE, expect=None)
+            out = p.communicate()[0]
+            assert p.returncode == 0, \
+                "qdstat exit status %s, output:\n%s" % (p.returncode, out)
+            if regexp: assert re.search(regexp, out, re.I), "Can't find '%s' in '%s'" % (regexp, out)
+            return out
+
+        def ssl_test(self, url_name, arg_names):
+            """Run simple SSL connection test with supplied parameters.
+            See test_ssl_* below.
+            """
+            args = dict(
+                trustfile = ['--ssl-trustfile', self.ssl_file('ca-certificate.pem')],
+                bad_trustfile = ['--ssl-trustfile', self.ssl_file('bad-ca-certificate.pem')],
+                client_cert = ['--ssl-certificate', self.ssl_file('client-certificate.pem')],
+                client_key = ['--ssl-key', self.ssl_file('client-private-key.pem')],
+                client_pass = ['--ssl-password', 'client-password'])
+            args['client_cert_all'] = args['client_cert'] + args['client_key'] + args['client_pass']
+
+            addrs = [self.router.addresses[i] for i in xrange(4)];
+            urls = dict(zip(['none', 'strict', 'unsecured', 'auth'], addrs) +
+                        zip(['none_s', 'strict_s', 'unsecured_s', 'auth_s'],
+                            (Url(a, scheme="amqps") for a in addrs)))
+
+            self.run_qdstat(['--general'] + sum([args[n] for n in arg_names], []),
+                            regexp=r'(?s)Router Statistics.*Mode\s*Standalone',
+                            address=str(urls[url_name]))
+
+        def ssl_test_bad(self, url_name, arg_names):
+            self.assertRaises(AssertionError, self.ssl_test, url_name, arg_names)
 
         # Non-SSL enabled listener should fail SSL connections.
-        self.do_test(none, [])
-        self.assertRaises(AssertionError, self.do_test, none_s, [])
-        self.assertRaises(AssertionError, self.do_test, none, client_cert)
+        def test_ssl_none(self):
+            self.ssl_test('none', [])
+
+        def test_ssl_scheme_to_none(self):
+            self.ssl_test_bad('none_s', [])
+
+        def test_ssl_cert_to_none(self):
+            self.ssl_test_bad('none', ['client_cert'])
 
         # Strict SSL listener, SSL only
-        self.assertRaises(AssertionError, self.do_test, strict, [])
-        self.do_test(strict_s, [])
-        self.do_test(strict_s, client_cert_all)
-        self.do_test(strict, client_cert_all)
-        self.do_test(strict, trustfile)
-        self.do_test(strict, trustfile + client_cert_all)
-        self.assertRaises(AssertionError, self.do_test, strict, bad_trustfile)
-
-        # Requre-auth SSL listener
-        self.assertRaises(AssertionError, self.do_test, auth, [])
-        self.assertRaises(AssertionError, self.do_test, auth_s, [])
-        self.assertRaises(AssertionError, self.do_test, auth, trustfile)
-        self.do_test(auth, client_cert_all)
-        self.do_test(auth, client_cert_all + trustfile)
-        self.assertRaises(AssertionError, self.do_test, auth, client_cert_all + bad_trustfile)
+        def test_ssl_none_to_strict(self):
+            self.ssl_test_bad('strict', [])
+
+        def test_ssl_schema_to_strict(self):
+            self.ssl_test('strict_s', [])
+
+        def test_ssl_cert_to_strict(self):
+            self.ssl_test('strict_s', ['client_cert_all'])
+
+        def test_ssl_trustfile_to_strict(self):
+            self.ssl_test('strict_s', ['trustfile'])
+
+        def test_ssl_trustfile_cert_to_strict(self):
+            self.ssl_test('strict_s', ['trustfile', 'client_cert_all'])
+
+        def test_ssl_bad_trustfile_to_strict(self):
+            self.ssl_test_bad('strict_s', ['bad_trustfile'])
+
+
+        # Require-auth SSL listener
+        def test_ssl_none_to_auth(self):
+            self.ssl_test_bad('auth', [])
+
+        def test_ssl_schema_to_auth(self):
+            self.ssl_test_bad('auth_s', [])
+
+        def test_ssl_trustfile_to_auth(self):
+            self.ssl_test_bad('auth_s', ['trustfile'])
+
+        def test_ssl_cert_to_auth(self):
+            self.ssl_test('auth_s', ['client_cert_all'])
+
+        def test_ssl_trustfile_cert_to_auth(self):
+            self.ssl_test('auth_s', ['trustfile', 'client_cert_all'])
+
+        def test_ssl_bad_trustfile_to_auth(self):
+            self.ssl_test_bad('auth_s', ['bad_trustfile', 'client_cert_all'])
+
 
         # Unsecured SSL listener, allows non-SSL
-        self.do_test(unsecured_s, [])
-        self.do_test(unsecured_s, client_cert_all)
-        self.do_test(unsecured_s, trustfile)
-        self.do_test(unsecured_s, client_cert_all + trustfile)
-        self.do_test(unsecured_s, [])
-        self.do_test(unsecured, []) # Allow unsecured
-        self.assertRaises(AssertionError, self.do_test, auth, client_cert_all + bad_trustfile)
+        def test_ssl_none_to_unsecured(self):
+            self.ssl_test('unsecured', [])
+
+        def test_ssl_schema_to_unsecured(self):
+            self.ssl_test('unsecured_s', [])
+
+        def test_ssl_cert_to_unsecured(self):
+            self.ssl_test('unsecured_s', ['client_cert_all'])
+
+        def test_ssl_trustfile_to_unsecured(self):
+            self.ssl_test('unsecured_s', ['trustfile'])
+
+        def test_ssl_trustfile_cert_to_unsecured(self):
+            self.ssl_test('unsecured_s', ['trustfile', 'client_cert_all'])
+
+        def test_ssl_bad_trustfile_to_unsecured(self):
+            self.ssl_test_bad('unsecured_s', ['bad_trustfile'])
+
+except SSLUnavailable:
+    class QdstatSslTest(system_test.TestCase):
+        def test_skip(self):
+            self.skipTest("Proton SSL support unavailable.")
+
 
 if __name__ == '__main__':
     unittest.main(system_test.main_module())



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