PROTON-1256: Track and check auto-detected protocol layers
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/391685a9 Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/391685a9 Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/391685a9 Branch: refs/heads/master Commit: 391685a9e922eb56ee2fd220ee3e904b2e28f5f6 Parents: f137151 Author: Andrew Stitcher <[email protected]> Authored: Thu Jul 14 10:57:30 2016 -0400 Committer: Andrew Stitcher <[email protected]> Committed: Thu Jul 14 15:17:43 2016 -0400 ---------------------------------------------------------------------- proton-c/src/engine/engine-internal.h | 11 +++++++++ proton-c/src/transport/transport.c | 27 +++++++++++++++++++++ tests/python/proton_tests/sasl.py | 38 +++++++++++++++++++++++++++++- 3 files changed, 75 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/391685a9/proton-c/src/engine/engine-internal.h ---------------------------------------------------------------------- diff --git a/proton-c/src/engine/engine-internal.h b/proton-c/src/engine/engine-internal.h index 0b052a7..761a840 100644 --- a/proton-c/src/engine/engine-internal.h +++ b/proton-c/src/engine/engine-internal.h @@ -110,6 +110,14 @@ extern const pn_io_layer_t ssl_layer; extern const pn_io_layer_t sasl_header_layer; extern const pn_io_layer_t sasl_write_header_layer; +// Bit flag defines for the protocol layers +typedef uint8_t pn_io_layer_flags_t; +#define LAYER_NONE 0 +#define LAYER_AMQP1 1 +#define LAYER_AMQPSASL 2 +#define LAYER_AMQPSSL 4 +#define LAYER_SSL 8 + typedef struct pni_sasl_t pni_sasl_t; typedef struct pni_ssl_t pni_ssl_t; @@ -193,6 +201,9 @@ struct pn_transport_t { uint16_t remote_channel_max; uint16_t channel_max; + pn_io_layer_flags_t allowed_layers; + pn_io_layer_flags_t present_layers; + bool freed; bool open_sent; bool open_rcvd; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/391685a9/proton-c/src/transport/transport.c ---------------------------------------------------------------------- diff --git a/proton-c/src/transport/transport.c b/proton-c/src/transport/transport.c index cc0bc2f..0d77977 100644 --- a/proton-c/src/transport/transport.c +++ b/proton-c/src/transport/transport.c @@ -273,6 +273,12 @@ ssize_t pn_io_layer_input_autodetect(pn_transport_t *transport, unsigned int lay pn_transport_logf(transport, "%s detected", pni_protocol_name(protocol)); switch (protocol) { case PNI_PROTOCOL_SSL: + if (!(transport->allowed_layers & LAYER_SSL)) { + error = "SSL protocol header not allowed (maybe detected twice)"; + break; + } + transport->present_layers |= LAYER_SSL; + transport->allowed_layers &= LAYER_AMQP1 | LAYER_AMQPSASL; if (!transport->ssl) { pn_ssl(transport); } @@ -280,6 +286,12 @@ ssize_t pn_io_layer_input_autodetect(pn_transport_t *transport, unsigned int lay transport->io_layers[layer+1] = &pni_autodetect_layer; return ssl_layer.process_input(transport, layer, bytes, available); case PNI_PROTOCOL_AMQP_SSL: + if (!(transport->allowed_layers & LAYER_AMQPSSL)) { + error = "AMQP SSL protocol header not allowed (maybe detected twice)"; + break; + } + transport->present_layers |= LAYER_AMQPSSL; + transport->allowed_layers &= LAYER_AMQP1 | LAYER_AMQPSASL; if (!transport->ssl) { pn_ssl(transport); } @@ -287,6 +299,12 @@ ssize_t pn_io_layer_input_autodetect(pn_transport_t *transport, unsigned int lay transport->io_layers[layer+1] = &pni_autodetect_layer; return 8; case PNI_PROTOCOL_AMQP_SASL: + if (!(transport->allowed_layers & LAYER_AMQPSASL)) { + error = "AMQP SASL protocol header not allowed (maybe detected twice)"; + break; + } + transport->present_layers |= LAYER_AMQPSASL; + transport->allowed_layers &= LAYER_AMQP1 | LAYER_AMQPSSL; if (!transport->sasl) { pn_sasl(transport); } @@ -297,6 +315,12 @@ ssize_t pn_io_layer_input_autodetect(pn_transport_t *transport, unsigned int lay pni_sasl_set_external_security(transport, pn_ssl_get_ssf((pn_ssl_t*)transport), pn_ssl_get_remote_subject((pn_ssl_t*)transport)); return 8; case PNI_PROTOCOL_AMQP1: + if (!(transport->allowed_layers & LAYER_AMQP1)) { + error = "AMQP1.0 protocol header not allowed (maybe detected twice)"; + break; + } + transport->present_layers |= LAYER_AMQP1; + transport->allowed_layers = LAYER_NONE; if (transport->auth_required && !pn_transport_is_authenticated(transport)) { pn_do_error(transport, "amqp:connection:policy-error", "Client skipped authentication - forbidden"); @@ -394,6 +418,9 @@ static void pn_transport_initialize(void *object) transport->io_layers[layer] = NULL; } + transport->allowed_layers = LAYER_AMQP1 | LAYER_AMQPSASL | LAYER_AMQPSSL | LAYER_SSL; + transport->present_layers = LAYER_NONE; + // Defer setting up the layers until the first data arrives or is sent transport->io_layers[0] = &pni_setup_layer; http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/391685a9/tests/python/proton_tests/sasl.py ---------------------------------------------------------------------- diff --git a/tests/python/proton_tests/sasl.py b/tests/python/proton_tests/sasl.py index 29b578d..0f5f525 100644 --- a/tests/python/proton_tests/sasl.py +++ b/tests/python/proton_tests/sasl.py @@ -84,7 +84,7 @@ def consumeAllOuput(t): stops = 0 while stops<1: out = t.peek(1024) - l = len(out) + l = len(out) if out else 0 t.pop(l) if l <= 0: stops += 1 @@ -103,6 +103,41 @@ class SaslTest(Test): # We have to generate the client frames manually because proton does not # generate pipelined SASL and AMQP frames together + def testIllegalProtocolLayering(self): + # TODO: Skip Proton-J for now + if "java" in sys.platform: + raise Skipped("Proton-J does not set error condition on protocol layering violation") + + # Server + self.s2.allowed_mechs('ANONYMOUS') + + c2 = Connection() + self.t2.bind(c2) + + assert self.s2.outcome is None + + # Push client bytes into server + self.t2.push(str2bin( + # SASL + 'AMQP\x03\x01\x00\x00' + # @sasl-init(65) [mechanism=:ANONYMOUS, initial-response=b"anonymous@fuschia"] + '\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia' + # SASL (again illegally) + 'AMQP\x03\x01\x00\x00' + # @sasl-init(65) [mechanism=:ANONYMOUS, initial-response=b"anonymous@fuschia"] + '\x00\x00\x002\x02\x01\x00\x00\x00SA\xd0\x00\x00\x00"\x00\x00\x00\x02\xa3\x09ANONYMOUS\xa0\x11anonymous@fuschia' + # AMQP + 'AMQP\x00\x01\x00\x00' + # @open(16) [container-id="", channel-max=1234] + '\x00\x00\x00!\x02\x00\x00\x00\x00S\x10\xd0\x00\x00\x00\x11\x00\x00\x00\x0a\xa1\x00@@`\x04\xd2@@@@@@' + )) + + consumeAllOuput(self.t2) + + assert self.t2.condition + assert self.t2.closed + assert not c2.state & Endpoint.REMOTE_ACTIVE + def testPipelinedClient(self): # TODO: When PROTON-1136 is fixed then remove this test if "java" in sys.platform: @@ -130,6 +165,7 @@ class SaslTest(Test): consumeAllOuput(self.t2) + assert not self.t2.condition assert self.s2.outcome == SASL.OK assert c2.state & Endpoint.REMOTE_ACTIVE --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
