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]

Reply via email to