You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by tr...@apache.org on 2014/08/06 16:24:26 UTC

svn commit: r1616245 - in /qpid/proton/trunk: proton-c/bindings/python/proton.py proton-c/include/proton/cproton.i proton-c/include/proton/sasl.h proton-c/src/sasl/sasl.c tests/python/proton_tests/sasl.py

Author: tross
Date: Wed Aug  6 14:24:25 2014
New Revision: 1616245

URL: http://svn.apache.org/r1616245
Log:
PROTON-485 - Make SASL server tolerant of clients that skip SASL

Modified:
    qpid/proton/trunk/proton-c/bindings/python/proton.py
    qpid/proton/trunk/proton-c/include/proton/cproton.i
    qpid/proton/trunk/proton-c/include/proton/sasl.h
    qpid/proton/trunk/proton-c/src/sasl/sasl.c
    qpid/proton/trunk/tests/python/proton_tests/sasl.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=1616245&r1=1616244&r2=1616245&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/bindings/python/proton.py (original)
+++ qpid/proton/trunk/proton-c/bindings/python/proton.py Wed Aug  6 14:24:25 2014
@@ -3131,6 +3131,7 @@ class SASL(object):
 
   OK = PN_SASL_OK
   AUTH = PN_SASL_AUTH
+  SKIPPED = PN_SASL_SKIPPED
 
   def __new__(cls, transport):
     """Enforce a singleton SASL object per Transport"""
@@ -3156,6 +3157,9 @@ class SASL(object):
   def server(self):
     pn_sasl_server(self._sasl)
 
+  def allow_skip(self, allow):
+    pn_sasl_allow_skip(self._sasl, allow)
+
   def plain(self, user, password):
     pn_sasl_plain(self._sasl, user, password)
 

Modified: qpid/proton/trunk/proton-c/include/proton/cproton.i
URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/include/proton/cproton.i?rev=1616245&r1=1616244&r2=1616245&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/include/proton/cproton.i (original)
+++ qpid/proton/trunk/proton-c/include/proton/cproton.i Wed Aug  6 14:24:25 2014
@@ -77,7 +77,7 @@ typedef long long int int64_t;
 
 %aggregate_check(int, check_sasl_outcome,
                  PN_SASL_NONE, PN_SASL_OK, PN_SASL_AUTH,
-                 PN_SASL_SYS, PN_SASL_PERM, PN_SASL_TEMP);
+                 PN_SASL_SYS, PN_SASL_PERM, PN_SASL_TEMP, PN_SASL_SKIPPED);
 
 %aggregate_check(int, check_sasl_state,
                  PN_SASL_CONF, PN_SASL_IDLE, PN_SASL_STEP,
@@ -999,6 +999,12 @@ typedef long long int int64_t;
   sasl != NULL;
 }
 
+%contract pn_sasl_allow_skip(pn_sasl_t *sasl, bool allow)
+{
+ require:
+  sasl != NULL;
+}
+
 %contract pn_sasl_plain(pn_sasl_t *sasl, const char *username, const char *password)
 {
  require:

Modified: qpid/proton/trunk/proton-c/include/proton/sasl.h
URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/include/proton/sasl.h?rev=1616245&r1=1616244&r2=1616245&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/include/proton/sasl.h (original)
+++ qpid/proton/trunk/proton-c/include/proton/sasl.h Wed Aug  6 14:24:25 2014
@@ -54,7 +54,8 @@ typedef enum {
   PN_SASL_AUTH=1,   /** failed due to bad credentials */
   PN_SASL_SYS=2,    /** failed due to a system error */
   PN_SASL_PERM=3,   /** failed due to unrecoverable error */
-  PN_SASL_TEMP=4    /** failed due to transient error */
+  PN_SASL_TEMP=4,   /** failed due to transient error */
+  PN_SASL_SKIPPED=5 /** the peer didn't perform the sasl exchange */
 } pn_sasl_outcome_t;
 
 /** The state of the SASL negotiation process */
@@ -113,6 +114,18 @@ PN_EXTERN void pn_sasl_client(pn_sasl_t 
  */
 PN_EXTERN void pn_sasl_server(pn_sasl_t *sasl);
 
+/** Configure a SASL server layer to permit the client to skip the SASL exchange.
+ *
+ * If the peer client skips the SASL exchange (i.e. goes right to the AMQP header)
+ * this server layer will succeed and result in the outcome of PN_SASL_SKIPPED.
+ * The default behavior is to fail and close the connection if the client skips
+ * SASL.
+ *
+ * @param[in] sasl the SASL layer to configure
+ * @param[in] allow true -> allow skip; false -> forbid skip
+ */
+    PN_EXTERN void pn_sasl_allow_skip(pn_sasl_t *sasl, bool allow);
+
 /** Configure the SASL layer to use the "PLAIN" mechanism.
  *
  * A utility function to configure a simple client SASL layer using

Modified: qpid/proton/trunk/proton-c/src/sasl/sasl.c
URL: http://svn.apache.org/viewvc/qpid/proton/trunk/proton-c/src/sasl/sasl.c?rev=1616245&r1=1616244&r2=1616245&view=diff
==============================================================================
--- qpid/proton/trunk/proton-c/src/sasl/sasl.c (original)
+++ qpid/proton/trunk/proton-c/src/sasl/sasl.c Wed Aug  6 14:24:25 2014
@@ -42,10 +42,10 @@ struct pn_sasl_t {
   char *remote_mechanisms;
   pn_buffer_t *send_data;
   pn_buffer_t *recv_data;
-  size_t header_count;
   pn_sasl_outcome_t outcome;
   bool client;
   bool configured;
+  bool allow_skip;
   bool sent_init;
   bool rcvd_init;
   bool sent_done;
@@ -71,6 +71,7 @@ pn_sasl_t *pn_sasl(pn_transport_t *trans
     sasl->send_data = pn_buffer(16);
     sasl->recv_data = pn_buffer(16);
     sasl->outcome = PN_SASL_NONE;
+    sasl->allow_skip = false;
     sasl->sent_init = false;
     sasl->rcvd_init = false;
     sasl->sent_done = false;
@@ -83,8 +84,6 @@ pn_sasl_t *pn_sasl(pn_transport_t *trans
     sasl->io_layer->process_input = pn_input_read_sasl_header;
     sasl->io_layer->process_output = pn_output_write_sasl_header;
     sasl->io_layer->process_tick = pn_io_layer_tick_passthru;
-
-    sasl->header_count = 0;
   }
 
   return transport->sasl;
@@ -173,6 +172,12 @@ void pn_sasl_server(pn_sasl_t *sasl)
   }
 }
 
+void pn_sasl_allow_skip(pn_sasl_t *sasl, bool allow)
+{
+  if (sasl)
+    sasl->allow_skip = allow;
+}
+
 void pn_sasl_plain(pn_sasl_t *sasl, const char *username, const char *password)
 {
   if (!sasl) return;
@@ -396,28 +401,44 @@ int pn_do_outcome(pn_dispatcher_t *disp)
 }
 
 #define SASL_HEADER ("AMQP\x03\x01\x00\x00")
+#define AMQP_HEADER ("AMQP\x00\x01\x00\x00")
 #define SASL_HEADER_LEN 8
 
 static ssize_t pn_input_read_sasl_header(pn_io_layer_t *io_layer, const char *bytes, size_t available)
 {
   pn_sasl_t *sasl = (pn_sasl_t *)io_layer->context;
-  const char *point = SASL_HEADER + sasl->header_count;
-  int delta = pn_min(available, SASL_HEADER_LEN - sasl->header_count);
-  if (!available || memcmp(bytes, point, delta)) {
-    char quoted[1024];
-    pn_quote_data(quoted, 1024, bytes, available);
-    pn_do_error(sasl->transport, "amqp:connection:framing-error",
-                "%s header mismatch: '%s'", "SASL", quoted);
-    return PN_EOS;
-  } else {
-    sasl->header_count += delta;
-    if (sasl->header_count == SASL_HEADER_LEN) {
-      sasl->io_layer->process_input = pn_input_read_sasl;
-      if (sasl->disp->trace & PN_TRACE_FRM)
-        pn_transport_logf(sasl->transport, "  <- %s", "SASL");
+  if (available > 0) {
+    if (available < SASL_HEADER_LEN) {
+      if (memcmp(bytes, SASL_HEADER, available) == 0 ||
+          memcmp(bytes, AMQP_HEADER, available) == 0)
+        return 0;
+    } else {
+      if (memcmp(bytes, SASL_HEADER, SASL_HEADER_LEN) == 0) {
+        sasl->io_layer->process_input = pn_input_read_sasl;
+        if (sasl->disp->trace & PN_TRACE_FRM)
+          pn_transport_logf(sasl->transport, "  <- %s", "SASL");
+        return SASL_HEADER_LEN;
+      }
+      if (memcmp(bytes, AMQP_HEADER, SASL_HEADER_LEN) == 0) {
+        if (sasl->allow_skip) {
+          sasl->outcome = PN_SASL_SKIPPED;
+          sasl->io_layer->process_input = pn_io_layer_input_passthru;
+          sasl->io_layer->process_output = pn_io_layer_output_passthru;
+          pn_io_layer_t *io_next = sasl->io_layer->next;
+          return io_next->process_input( io_next, bytes, available );
+        } else {
+            pn_do_error(sasl->transport, "amqp:connection:policy-error",
+                        "Client skipped SASL exchange - forbidden");
+            return PN_EOS;
+        }
+      }
     }
-    return delta;
   }
+  char quoted[1024];
+  pn_quote_data(quoted, 1024, bytes, available);
+  pn_do_error(sasl->transport, "amqp:connection:framing-error",
+              "%s header mismatch: '%s'", "SASL", quoted);
+  return PN_EOS;
 }
 
 static ssize_t pn_input_read_sasl(pn_io_layer_t *io_layer, const char *bytes, size_t available)

Modified: qpid/proton/trunk/tests/python/proton_tests/sasl.py
URL: http://svn.apache.org/viewvc/qpid/proton/trunk/tests/python/proton_tests/sasl.py?rev=1616245&r1=1616244&r2=1616245&view=diff
==============================================================================
--- qpid/proton/trunk/tests/python/proton_tests/sasl.py (original)
+++ qpid/proton/trunk/tests/python/proton_tests/sasl.py Wed Aug  6 14:24:25 2014
@@ -191,3 +191,13 @@ class SaslTest(Test):
       sasl1 = transport.sasl()
       sasl2 = SASL(transport)
       assert sasl1 is sasl2
+
+  def testSaslSkipped(self):
+    """Verify that the server (with SASL) correctly handles a client without SASL"""
+    self.t1 = Transport()
+    self.s2.mechanisms("ANONYMOUS")
+    self.s2.server()
+    self.s2.allow_skip(True)
+    self.pump()
+    assert self.s2.outcome == SASL.SKIPPED
+



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