qpid-proton git commit: PROTON-1500: Implement plugin interface for SASL implementations - Allow sasl implementation to be selected per connection - Compile both default and cyrus implementation (if we can) - No current change in behaviour (get cyrus is

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

qpid-proton git commit: PROTON-1500: Implement plugin interface for SASL implementations - Allow sasl implementation to be selected per connection - Compile both default and cyrus implementation (if we can) - No current change in behaviour (get cyrus is

astitcher
Repository: qpid-proton
Updated Branches:
  refs/heads/PROTON-1500 [created] 21a74d980


PROTON-1500: Implement plugin interface for SASL implementations
- Allow sasl implementation to be selected per connection
- Compile both default and cyrus implementation (if we can)
- No current change in behaviour (get cyrus is available and default otherwise)
- But now have frqamework to solve PROTON-1209; Stil to do for this:
- Need to have API to turn on cyrus, with default being the initial implementation
- Need to deprecate cyrus specific APIs:
-- pn_sasl_extended
-- pn_sasl_config_path
-- pn_sasl_config_name
- Need to deprecate pn_sasl_done


Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/21a74d98
Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/21a74d98
Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/21a74d98

Branch: refs/heads/PROTON-1500
Commit: 21a74d980298d3eeb5b4c57c93815a5e6643cb38
Parents: c31e2ec
Author: Andrew Stitcher <[hidden email]>
Authored: Wed Jun 7 01:42:28 2017 -0400
Committer: Andrew Stitcher <[hidden email]>
Committed: Fri Jun 9 18:27:31 2017 -0400

----------------------------------------------------------------------
 proton-c/CMakeLists.txt               |   8 +-
 proton-c/bindings/python/setup.py.in  |   4 +-
 proton-c/include/proton/sasl-plugin.h | 138 ++++++++++
 proton-c/src/core/config.h            |   4 -
 proton-c/src/sasl/cyrus_sasl.c        | 334 +++++++++++++----------
 proton-c/src/sasl/cyrus_stub.c        |  40 +++
 proton-c/src/sasl/default_sasl.c      | 248 +++++++++++++++++
 proton-c/src/sasl/none_sasl.c         | 197 --------------
 proton-c/src/sasl/sasl-internal.h     |  46 +---
 proton-c/src/sasl/sasl.c              | 415 +++++++++++++++++++++--------
 10 files changed, 940 insertions(+), 494 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/proton-c/CMakeLists.txt b/proton-c/CMakeLists.txt
index a38664b..612da1c 100644
--- a/proton-c/CMakeLists.txt
+++ b/proton-c/CMakeLists.txt
@@ -107,11 +107,11 @@ endif(PN_WINAPI)
 
 # Link in SASL if present
 if (SASL_IMPL STREQUAL cyrus)
-  set(pn_sasl_impl src/sasl/sasl.c src/sasl/cyrus_sasl.c)
+  set(pn_sasl_impl src/sasl/sasl.c src/sasl/default_sasl.c src/sasl/cyrus_sasl.c)
   include_directories (${CYRUS_SASL_INCLUDE_DIR})
   set(SASL_LIB ${CYRUS_SASL_LIBRARY} -lpthread)
 elseif (SASL_IMPL STREQUAL none)
-  set(pn_sasl_impl src/sasl/sasl.c src/sasl/none_sasl.c)
+  set(pn_sasl_impl src/sasl/sasl.c src/sasl/default_sasl.c src/sasl/cyrus_stub.c)
 endif ()
 
 # Set Compiler extra flags for Solaris when using SunStudio
@@ -334,8 +334,9 @@ set (qpid-proton-platform-io
 # for full source distribution:
 set (qpid-proton-layers-all
   src/sasl/sasl.c
+  src/sasl/default_sasl.c
   src/sasl/cyrus_sasl.c
-  src/sasl/none_sasl.c
+  src/sasl/cyrus_stub.c
   src/ssl/openssl.c
   src/ssl/schannel.c
   src/ssl/ssl_stub.c
@@ -458,6 +459,7 @@ set (qpid-proton-include
   include/proton/object.h
   include/proton/proactor.h
   include/proton/sasl.h
+  include/proton/sasl-plugin.h
   include/proton/session.h
   include/proton/ssl.h
   include/proton/terminus.h

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/bindings/python/setup.py.in
----------------------------------------------------------------------
diff --git a/proton-c/bindings/python/setup.py.in b/proton-c/bindings/python/setup.py.in
index a478a5d..f2516fc 100755
--- a/proton-c/bindings/python/setup.py.in
+++ b/proton-c/bindings/python/setup.py.in
@@ -144,7 +144,7 @@ class Configure(build_ext):
                   'schannel.c',
                   'ssl_stub.c',
                   'cyrus_sasl.c',
-                  'none_sasl.c',
+                  'cyrus_stub.c',
                   'libuv.c']
         sources = filter(lambda x: os.path.basename(x) not in cfgdep, sources)
         sources = [os.path.join(proton_base, x) for x in sources]
@@ -190,9 +190,9 @@ class Configure(build_ext):
             libraries.append('sasl2')
             sources.append(os.path.join(proton_src, 'sasl', 'cyrus_sasl.c'))
         else:
-            sources.append(os.path.join(proton_src, 'sasl', 'none_sasl.c'))
             log.warn("Cyrus SASL not installed - only the ANONYMOUS and"
                      " PLAIN mechanisms will be supported!")
+            sources.append(os.path.join(proton_src, 'sasl', 'cyrus_stub.c'))
 
         # Include proactor if libuv is available
         if cc.has_function('uv_tcp_init', includes=['uv.h'],

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/include/proton/sasl-plugin.h
----------------------------------------------------------------------
diff --git a/proton-c/include/proton/sasl-plugin.h b/proton-c/include/proton/sasl-plugin.h
new file mode 100644
index 0000000..cbc6684
--- /dev/null
+++ b/proton-c/include/proton/sasl-plugin.h
@@ -0,0 +1,138 @@
+#ifndef PROTON_SASL_PLUGIN_H
+#define PROTON_SASL_PLUGIN_H 1
+
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include <proton/import_export.h>
+#include <proton/type_compat.h>
+#include <proton/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Internal SASL authenticator interface: These are the entry points to a SASL implementations
+//
+// Free up all data structures allocated by the SASL implementation
+// void free(pn_transport_t *transport);
+//
+// Return space separated list of supported mechanisms (client and server)
+// If the returned string is dynamically allocated by the SASL implemetation
+// it must stay valid until the free entry point is called.
+// const char *list_mechs(pn_transport_t *transport);
+//
+// Initialise for either client or server (can't call both for a
+// given transport/connection):
+// bool init_server(pn_transport_t *transport);
+// bool init_client(pn_transport_t *transport);
+//
+// Writing:
+// void prepare_write(pn_transport_t *transport);
+//
+// Reading:
+// Server side (process server SASL messages):
+// void process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv);
+// void process_response(pn_transport_t *transport, const pn_bytes_t *recv);
+//
+// Client side (process client SASL messages)
+// bool process_mechanisms(pn_transport_t *transport, const char *mechs);
+// void process_challenge(pn_transport_t *transport, const pn_bytes_t *recv);
+// void process_outcome(pn_transport_t *transport);
+
+// Security layer interface (active after SASL succeeds)
+// bool    can_encrypt(pn_transport_t *transport);
+// ssize_t max_encrypt_size(pn_transport_t *transport);
+// ssize_t encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+// ssize_t decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+
+typedef struct pnx_sasl_implementation
+{
+    void (*free)(pn_transport_t *transport);
+
+    const char*  (*list_mechs)(pn_transport_t *transport);
+
+    bool (*init_server)(pn_transport_t *transport);
+    bool (*init_client)(pn_transport_t *transport);
+
+    void (*prepare_write)(pn_transport_t *transport);
+
+    void (*process_init)(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv);
+    void (*process_response)(pn_transport_t *transport, const pn_bytes_t *recv);
+
+    bool (*process_mechanisms)(pn_transport_t *transport, const char *mechs);
+    void (*process_challenge)(pn_transport_t *transport, const pn_bytes_t *recv);
+    void (*process_outcome)(pn_transport_t *transport);
+
+    bool    (*can_encrypt)(pn_transport_t *transport);
+    ssize_t (*max_encrypt_size)(pn_transport_t *transport);
+    ssize_t (*encode)(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+    ssize_t (*decode)(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+
+} pnx_sasl_implementation;
+
+// Shared SASL API used by the actual SASL authenticators
+enum pnx_sasl_state {
+  SASL_NONE,
+  SASL_POSTED_INIT,
+  SASL_POSTED_MECHANISMS,
+  SASL_POSTED_RESPONSE,
+  SASL_POSTED_CHALLENGE,
+  SASL_RECVED_OUTCOME_SUCCEED,
+  SASL_RECVED_OUTCOME_FAIL,
+  SASL_POSTED_OUTCOME,
+  SASL_ERROR
+};
+
+// APIs used by sasl implementations
+PN_EXTERN void  pnx_sasl_logf(pn_transport_t *transport, const char *format, ...);
+
+PN_EXTERN void *pnx_sasl_get_context(pn_transport_t *transport);
+PN_EXTERN void  pnx_sasl_set_context(pn_transport_t *transport, void *context);
+
+PN_EXTERN bool  pnx_sasl_is_client(pn_transport_t *transport);
+PN_EXTERN bool  pnx_sasl_is_included_mech(pn_transport_t *transport, pn_bytes_t s);
+PN_EXTERN bool  pnx_sasl_is_transport_encrypted(pn_transport_t *transport);
+PN_EXTERN bool  pnx_sasl_get_allow_insecure_mechs(pn_transport_t *transport);
+PN_EXTERN bool  pnx_sasl_get_auth_required(pn_transport_t *transport);
+PN_EXTERN const char *pnx_sasl_get_external_username(pn_transport_t *transport);
+PN_EXTERN int   pnx_sasl_get_external_ssf(pn_transport_t *transport);
+
+PN_EXTERN const char *pnx_sasl_get_username(pn_transport_t *transport);
+PN_EXTERN const char *pnx_sasl_get_password(pn_transport_t *transport);
+PN_EXTERN void  pnx_sasl_clear_password(pn_transport_t *transport);
+PN_EXTERN const char *pnx_sasl_get_remote_fqdn(pn_transport_t *transport);
+PN_EXTERN const char *pnx_sasl_get_selected_mechanism(pn_transport_t *transport);
+
+PN_EXTERN void  pnx_sasl_set_bytes_out(pn_transport_t *transport, pn_bytes_t bytes);
+PN_EXTERN void  pnx_sasl_set_desired_state(pn_transport_t *transport, enum pnx_sasl_state desired_state);
+PN_EXTERN void  pnx_sasl_set_selected_mechanism(pn_transport_t *transport, const char *mechanism);
+PN_EXTERN void  pnx_sasl_succeed_authentication(pn_transport_t *transport, const char *username);
+PN_EXTERN void  pnx_sasl_fail_authentication(pn_transport_t *transport);
+
+PN_EXTERN void  pnx_sasl_set_implementation(pn_transport_t *transport, const pnx_sasl_implementation *impl, void *context);
+PN_EXTERN void  pnx_sasl_set_default_implementation(const pnx_sasl_implementation *impl);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* sasl-plugin.h */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/src/core/config.h
----------------------------------------------------------------------
diff --git a/proton-c/src/core/config.h b/proton-c/src/core/config.h
index 5a2e7bc..d9a1e82 100644
--- a/proton-c/src/core/config.h
+++ b/proton-c/src/core/config.h
@@ -25,8 +25,4 @@
 # define PN_TRANSPORT_INITIAL_FRAME_SIZE (512) /* bytes */
 #endif
 
-#ifndef PN_SASL_MAX_BUFFSIZE
-# define PN_SASL_MAX_BUFFSIZE (32768) /* bytes */
-#endif
-
 #endif /*  _PROTON_SRC_CONFIG_H */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/src/sasl/cyrus_sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/cyrus_sasl.c b/proton-c/src/sasl/cyrus_sasl.c
index ef1db9d..ae910f4 100644
--- a/proton-c/src/sasl/cyrus_sasl.c
+++ b/proton-c/src/sasl/cyrus_sasl.c
@@ -18,15 +18,69 @@
  * under the License.
  *
  */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
 
-#include "core/config.h"
-#include "core/engine-internal.h"
-#include "sasl-internal.h"
-
+#include "proton/sasl.h"
+#include "proton/sasl-plugin.h"
+#include "proton/transport.h"
 
 #include <sasl/sasl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
 #include <pthread.h>
 
+#ifndef CYRUS_SASL_MAX_BUFFSIZE
+# define CYRUS_SASL_MAX_BUFFSIZE (32768) /* bytes */
+#endif
+
+// SASL implementation entry points
+static void cyrus_sasl_prepare(pn_transport_t *transport);
+static void cyrus_sasl_free(pn_transport_t *transport);
+static const char *cyrus_sasl_list_mechs(pn_transport_t *transport);
+
+static bool cyrus_sasl_init_server(pn_transport_t *transport);
+static void cyrus_sasl_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv);
+static void cyrus_sasl_process_response(pn_transport_t *transport, const pn_bytes_t *recv);
+
+static bool cyrus_sasl_init_client(pn_transport_t *transport);
+static bool cyrus_sasl_process_mechanisms(pn_transport_t *transport, const char *mechs);
+static void cyrus_sasl_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv);
+static void cyrus_sasl_process_outcome(pn_transport_t *transport);
+
+static bool cyrus_sasl_can_encrypt(pn_transport_t *transport);
+static ssize_t cyrus_sasl_max_encrypt_size(pn_transport_t *transport);
+static ssize_t cyrus_sasl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+static ssize_t cyrus_sasl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+
+static const pnx_sasl_implementation sasl_impl = {
+    cyrus_sasl_free,
+
+    cyrus_sasl_list_mechs,
+
+    cyrus_sasl_init_server,
+    cyrus_sasl_init_client,
+
+    cyrus_sasl_prepare,
+
+    cyrus_sasl_process_init,
+    cyrus_sasl_process_response,
+
+    cyrus_sasl_process_mechanisms,
+    cyrus_sasl_process_challenge,
+    cyrus_sasl_process_outcome,
+
+    cyrus_sasl_can_encrypt,
+    cyrus_sasl_max_encrypt_size,
+    cyrus_sasl_encode,
+    cyrus_sasl_decode
+};
+
+extern const pnx_sasl_implementation * const cyrus_sasl_impl;
+const pnx_sasl_implementation * const cyrus_sasl_impl = &sasl_impl;
+
 // If the version of Cyrus SASL is too early for sasl_client_done()/sasl_server_done()
 // don't do any global clean up as it's not safe to use just sasl_done() for an
 // executable that uses both client and server parts of Cyrus SASL, because it can't
@@ -43,8 +97,7 @@ static bool pni_check_sasl_result(sasl_conn_t *conn, int r, pn_transport_t *logg
     if (r==SASL_OK) return true;
 
     const char* err = conn ? sasl_errdetail(conn) : sasl_errstring(r, NULL, NULL);
-    if (logger->trace & PN_TRACE_DRV)
-        pn_transport_logf(logger, "sasl error: %s", err);
+    pnx_sasl_logf(logger, "sasl error: %s", err);
     pn_condition_t* c = pn_transport_condition(logger);
     pn_condition_set_name(c, "proton:io:sasl_error");
     pn_condition_set_description(c, err);
@@ -52,7 +105,7 @@ static bool pni_check_sasl_result(sasl_conn_t *conn, int r, pn_transport_t *logg
 }
 
 // Cyrus wrappers
-static void pni_cyrus_interact(pni_sasl_t *sasl, sasl_interact_t *interact)
+static void pni_cyrus_interact(pn_transport_t *transport, sasl_interact_t *interact)
 {
   for (sasl_interact_t *i = interact; i->id!=SASL_CB_LIST_END; i++) {
     switch (i->id) {
@@ -60,36 +113,34 @@ static void pni_cyrus_interact(pni_sasl_t *sasl, sasl_interact_t *interact)
       i->result = 0;
       i->len = 0;
       break;
-    case SASL_CB_AUTHNAME:
-      i->result = sasl->username;
-      i->len = strlen(sasl->username);
+    case SASL_CB_AUTHNAME: {
+      const char *username = pnx_sasl_get_username(transport);
+      i->result = username;
+      i->len = strlen(username);
       break;
-    case SASL_CB_PASS:
-      i->result = sasl->password;
-      i->len = strlen(sasl->password);
+    }
+    case SASL_CB_PASS: {
+      const char *password = pnx_sasl_get_password(transport);
+      i->result = password;
+      i->len = strlen(password);
       break;
+    }
     default:
       fprintf(stderr, "(%s): %s - %s\n", i->challenge, i->prompt, i->defresult);
     }
   }
 }
 
-int pni_sasl_impl_list_mechs(pn_transport_t *transport, char **mechlist)
+const char *cyrus_sasl_list_mechs(pn_transport_t *transport)
 {
-  pni_sasl_t *sasl = transport->sasl;
-  sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+  sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
+  if (!cyrus_conn) return NULL;
+
   int count = 0;
-  if (cyrus_conn) {
-    const char *result = NULL;
-
-    int r = sasl_listmech(cyrus_conn, NULL, "", " ", "", &result, NULL, &count);
-    if (pni_check_sasl_result(cyrus_conn, r, transport)) {
-      if (result && *result) {
-        *mechlist = pn_strdup(result);
-      }
-    }
-  }
-  return count;
+  const char *result = NULL;
+  int r = sasl_listmech(cyrus_conn, NULL, "", " ", "", &result, NULL, &count);
+  pni_check_sasl_result(cyrus_conn, r, transport);
+  return result;
 }
 
 // Set up callbacks to use interact
@@ -115,6 +166,25 @@ static pthread_mutex_t pni_cyrus_mutex = PTHREAD_MUTEX_INITIALIZER;
 static bool pni_cyrus_client_started = false;
 static bool pni_cyrus_server_started = false;
 
+bool pn_sasl_extended(void)
+{
+  return true;
+}
+
+void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name)
+{
+    if (!pni_cyrus_config_name) {
+      pni_cyrus_config_name = strdup(name);
+    }
+}
+
+void pn_sasl_config_path(pn_sasl_t *sasl0, const char *dir)
+{
+    if (!pni_cyrus_config_dir) {
+      pni_cyrus_config_dir = strdup(dir);
+    }
+}
+
 __attribute__((destructor))
 static void pni_cyrus_finish(void) {
   pthread_mutex_lock(&pni_cyrus_mutex);
@@ -131,6 +201,11 @@ static void pni_cyrus_client_once(void) {
   int result = SASL_OK;
   if (pni_cyrus_config_dir) {
     result = sasl_set_path(SASL_PATH_TYPE_CONFIG, pni_cyrus_config_dir);
+  } else {
+    char *config_dir = getenv("PN_SASL_CONFIG_PATH");
+    if (config_dir) {
+      result = sasl_set_path(SASL_PATH_TYPE_CONFIG, config_dir);
+    }
   }
   if (result==SASL_OK) {
     result = sasl_client_init(NULL);
@@ -146,6 +221,11 @@ static void pni_cyrus_server_once(void) {
   int result = SASL_OK;
   if (pni_cyrus_config_dir) {
     result = sasl_set_path(SASL_PATH_TYPE_CONFIG, pni_cyrus_config_dir);
+  } else {
+    char *config_dir = getenv("PN_SASL_CONFIG_PATH");
+    if (config_dir) {
+      result = sasl_set_path(SASL_PATH_TYPE_CONFIG, config_dir);
+    }
   }
   if (result==SASL_OK) {
     result = sasl_server_init(NULL, pni_cyrus_config_name ? pni_cyrus_config_name : default_config_name);
@@ -157,69 +237,67 @@ static void pni_cyrus_server_once(void) {
 
 static pthread_once_t pni_cyrus_client_init = PTHREAD_ONCE_INIT;
 static void pni_cyrus_client_start(void) {
-    pthread_once(&pni_cyrus_client_init, pni_cyrus_client_once);
+  pthread_once(&pni_cyrus_client_init, pni_cyrus_client_once);
 }
 static pthread_once_t pni_cyrus_server_init = PTHREAD_ONCE_INIT;
 static void pni_cyrus_server_start(void) {
   pthread_once(&pni_cyrus_server_init, pni_cyrus_server_once);
 }
 
-bool pni_init_client(pn_transport_t* transport) {
-  pni_sasl_t *sasl = transport->sasl;
+void cyrus_sasl_prepare(pn_transport_t* transport)
+{
+}
+
+bool cyrus_sasl_init_client(pn_transport_t* transport) {
   int result;
   sasl_conn_t *cyrus_conn = NULL;
   do {
-    // If pni_cyrus_config_dir already set then we already called pni_cyrus_client_start or pni_cyrus_server_start
-    // and the directory is already fixed - don't change
-    if (sasl->config_dir && !pni_cyrus_config_dir) {
-      pni_cyrus_config_dir = pn_strdup(sasl->config_dir);
-    }
-
     pni_cyrus_client_start();
     result = pni_cyrus_client_init_rc;
     if (result!=SASL_OK) break;
 
-    const sasl_callback_t *callbacks = sasl->username ? sasl->password ? pni_user_password_callbacks : pni_user_callbacks : NULL;
+    const sasl_callback_t *callbacks =
+      pnx_sasl_get_username(transport) ? pnx_sasl_get_password(transport) ? pni_user_password_callbacks : pni_user_callbacks : NULL;
     result = sasl_client_new(amqp_service,
-                             sasl->remote_fqdn,
+                             pnx_sasl_get_remote_fqdn(transport),
                              NULL, NULL,
                              callbacks, 0,
                              &cyrus_conn);
     if (result!=SASL_OK) break;
-    sasl->impl_context = cyrus_conn;
+    pnx_sasl_set_context(transport, cyrus_conn);
 
     sasl_security_properties_t secprops = {0};
     secprops.security_flags =
-      ( sasl->allow_insecure_mechs ? 0 : SASL_SEC_NOPLAINTEXT ) |
-      ( transport->auth_required ? SASL_SEC_NOANONYMOUS : 0 ) ;
+      ( pnx_sasl_get_allow_insecure_mechs(transport) ? 0 : SASL_SEC_NOPLAINTEXT ) |
+      ( pnx_sasl_get_auth_required(transport) ? SASL_SEC_NOANONYMOUS : 0 ) ;
     secprops.min_ssf = 0;
     secprops.max_ssf = 2048;
-    secprops.maxbufsize = PN_SASL_MAX_BUFFSIZE;
+    secprops.maxbufsize = CYRUS_SASL_MAX_BUFFSIZE;
 
     result = sasl_setprop(cyrus_conn, SASL_SEC_PROPS, &secprops);
     if (result!=SASL_OK) break;
 
-    sasl_ssf_t ssf = sasl->external_ssf;
+    sasl_ssf_t ssf = pnx_sasl_get_external_ssf(transport);
     result = sasl_setprop(cyrus_conn, SASL_SSF_EXTERNAL, &ssf);
     if (result!=SASL_OK) break;
 
-    const char *extid = sasl->external_auth;
+    const char *extid = pnx_sasl_get_external_username(transport);
     if (extid) {
       result = sasl_setprop(cyrus_conn, SASL_AUTH_EXTERNAL, extid);
     }
   } while (false);
-  cyrus_conn = (sasl_conn_t*) sasl->impl_context;
+  cyrus_conn = (sasl_conn_t*) pnx_sasl_get_context(transport);
   return pni_check_sasl_result(cyrus_conn, result, transport);
 }
 
-static int pni_wrap_client_start(pni_sasl_t *sasl, const char *mechs, const char **mechusing)
+static int pni_wrap_client_start(pn_transport_t *transport, const char *mechs, const char **mechusing)
 {
     int result;
     sasl_interact_t *client_interact=NULL;
     const char *out;
     unsigned outlen;
 
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
     do {
 
         result = sasl_client_start(cyrus_conn,
@@ -228,25 +306,24 @@ static int pni_wrap_client_start(pni_sasl_t *sasl, const char *mechs, const char
                                    &out, &outlen,
                                    mechusing);
         if (result==SASL_INTERACT) {
-            pni_cyrus_interact(sasl, client_interact);
+            pni_cyrus_interact(transport, client_interact);
         }
     } while (result==SASL_INTERACT);
 
-    sasl->bytes_out.start = out;
-    sasl->bytes_out.size = outlen;
+    pnx_sasl_set_bytes_out(transport, pn_bytes(outlen, out));
     return result;
 }
 
-bool pni_process_mechanisms(pn_transport_t *transport, const char *mechs)
+bool cyrus_sasl_process_mechanisms(pn_transport_t *transport, const char *mechs)
 {
-    pni_sasl_t *sasl = transport->sasl;
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
     const char *mech_selected;
-    int result = pni_wrap_client_start(sasl, mechs, &mech_selected);
+    int result = pni_wrap_client_start(transport, mechs, &mech_selected);
     switch (result) {
         case SASL_OK:
         case SASL_CONTINUE:
-          sasl->selected_mechanism = pn_strdup(mech_selected);
+          pnx_sasl_set_selected_mechanism(transport, mech_selected);
+          pnx_sasl_set_desired_state(transport, SASL_POSTED_INIT);
           return true;
         case SASL_NOMECH:
         default:
@@ -256,9 +333,9 @@ bool pni_process_mechanisms(pn_transport_t *transport, const char *mechs)
 }
 
 
-static int pni_wrap_client_step(pni_sasl_t *sasl, const pn_bytes_t *in)
+static int pni_wrap_client_step(pn_transport_t *transport, const pn_bytes_t *in)
 {
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
     sasl_interact_t *client_interact=NULL;
     const char *out;
     unsigned outlen;
@@ -271,94 +348,89 @@ static int pni_wrap_client_step(pni_sasl_t *sasl, const pn_bytes_t *in)
                                   &client_interact,
                                   &out, &outlen);
         if (result==SASL_INTERACT) {
-            pni_cyrus_interact(sasl, client_interact);
+            pni_cyrus_interact(transport, client_interact);
         }
     } while (result==SASL_INTERACT);
 
-    sasl->bytes_out.start = out;
-    sasl->bytes_out.size = outlen;
+    pnx_sasl_set_bytes_out(transport, pn_bytes(outlen, out));
     return result;
 }
 
-void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv)
+void cyrus_sasl_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv)
 {
-    pni_sasl_t *sasl = transport->sasl;
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
-    int result = pni_wrap_client_step(sasl, recv);
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
+    int result = pni_wrap_client_step(transport, recv);
     switch (result) {
         case SASL_OK:
             // Authenticated
             // TODO: Documented that we need to call sasl_client_step() again to be sure!;
         case SASL_CONTINUE:
             // Need to send a response
-            pni_sasl_set_desired_state(transport, SASL_POSTED_RESPONSE);
+            pnx_sasl_set_desired_state(transport, SASL_POSTED_RESPONSE);
             break;
         default:
             pni_check_sasl_result(cyrus_conn, result, transport);
 
             // Failed somehow - equivalent to failing authentication
-            sasl->outcome = PN_SASL_AUTH;
-            pni_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL);
+            pnx_sasl_fail_authentication(transport);
+            pnx_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL);
             break;
     }
 }
 
-bool pni_init_server(pn_transport_t* transport)
+void cyrus_sasl_process_outcome(pn_transport_t* transport)
+{
+}
+
+bool cyrus_sasl_init_server(pn_transport_t* transport)
 {
-  pni_sasl_t *sasl = transport->sasl;
   int result;
   sasl_conn_t *cyrus_conn = NULL;
   do {
-    // If pni_cyrus_config_dir already set then we already called pni_cyrus_client_start or pni_cyrus_server_start
-    // and the directory is already fixed - don't change
-    if (sasl->config_dir && !pni_cyrus_config_dir) {
-      pni_cyrus_config_dir = pn_strdup(sasl->config_dir);
-    }
-
-    // If pni_cyrus_config_name already set then we already called pni_cyrus_server_start
-    // and the name is already fixed - don't change
-    if (sasl->config_name && !pni_cyrus_config_name) {
-      pni_cyrus_config_name = pn_strdup(sasl->config_name);
-    }
-
     pni_cyrus_server_start();
     result = pni_cyrus_server_init_rc;
     if (result!=SASL_OK) break;
 
     result = sasl_server_new(amqp_service, NULL, NULL, NULL, NULL, NULL, 0, &cyrus_conn);
     if (result!=SASL_OK) break;
-    sasl->impl_context = cyrus_conn;
+    pnx_sasl_set_context(transport, cyrus_conn);
 
     sasl_security_properties_t secprops = {0};
     secprops.security_flags =
-      ( sasl->allow_insecure_mechs ? 0 : SASL_SEC_NOPLAINTEXT ) |
-      ( transport->auth_required ? SASL_SEC_NOANONYMOUS : 0 ) ;
+      ( pnx_sasl_get_allow_insecure_mechs(transport) ? 0 : SASL_SEC_NOPLAINTEXT ) |
+      ( pnx_sasl_get_auth_required(transport) ? SASL_SEC_NOANONYMOUS : 0 ) ;
     secprops.min_ssf = 0;
     secprops.max_ssf = 2048;
-    secprops.maxbufsize = PN_SASL_MAX_BUFFSIZE;
+    secprops.maxbufsize = CYRUS_SASL_MAX_BUFFSIZE;
 
     result = sasl_setprop(cyrus_conn, SASL_SEC_PROPS, &secprops);
     if (result!=SASL_OK) break;
 
-    sasl_ssf_t ssf = sasl->external_ssf;
+    sasl_ssf_t ssf = pnx_sasl_get_external_ssf(transport);
     result = sasl_setprop(cyrus_conn, SASL_SSF_EXTERNAL, &ssf);
     if (result!=SASL_OK) break;
 
-    const char *extid = sasl->external_auth;
+    const char *extid = pnx_sasl_get_external_username(transport);
     if (extid) {
     result = sasl_setprop(cyrus_conn, SASL_AUTH_EXTERNAL, extid);
     }
   } while (false);
-  cyrus_conn = (sasl_conn_t*) sasl->impl_context;
-  return pni_check_sasl_result(cyrus_conn, result, transport);
+  cyrus_conn = (sasl_conn_t*) pnx_sasl_get_context(transport);
+  if (pni_check_sasl_result(cyrus_conn, result, transport)) {
+      // Setup to send SASL mechanisms frame
+      pnx_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS);
+      return true;
+  } else {
+      return false;
+  }
 }
 
-static int pni_wrap_server_start(pni_sasl_t *sasl, const char *mech_selected, const pn_bytes_t *in)
+static int pni_wrap_server_start(pn_transport_t *transport, const char *mech_selected, const pn_bytes_t *in)
 {
     int result;
     const char *out;
     unsigned outlen;
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
     const char *in_bytes = in->start;
     size_t in_size = in->size;
     // Interop hack for ANONYMOUS - some of the earlier versions of proton will send and no data
@@ -374,53 +446,48 @@ static int pni_wrap_server_start(pni_sasl_t *sasl, const char *mech_selected, co
                                in_bytes, in_size,
                                &out, &outlen);
 
-    sasl->bytes_out.start = out;
-    sasl->bytes_out.size = outlen;
+    pnx_sasl_set_bytes_out(transport, pn_bytes(outlen, out));
     return result;
 }
 
 static void pni_process_server_result(pn_transport_t *transport, int result)
 {
-    pni_sasl_t *sasl = transport->sasl;
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
     switch (result) {
-        case SASL_OK:
+        case SASL_OK: {
             // Authenticated
-            sasl->outcome = PN_SASL_OK;
-            transport->authenticated = true;
             // Get username from SASL
             const void* value;
             sasl_getprop(cyrus_conn, SASL_USERNAME, &value);
-            sasl->username = (const char*) value;
-            if (transport->trace & PN_TRACE_DRV)
-              pn_transport_logf(transport, "Authenticated user: %s with mechanism %s", sasl->username, sasl->selected_mechanism);
-            pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
+            pnx_sasl_succeed_authentication(transport, (const char*) value);
+            pnx_sasl_logf(transport, "Authenticated user: %s with mechanism %s",
+                          pnx_sasl_get_username(transport), pnx_sasl_get_selected_mechanism(transport));
+            pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
             break;
+        }
         case SASL_CONTINUE:
             // Need to send a challenge
-            pni_sasl_set_desired_state(transport, SASL_POSTED_CHALLENGE);
+            pnx_sasl_set_desired_state(transport, SASL_POSTED_CHALLENGE);
             break;
         default:
             pni_check_sasl_result(cyrus_conn, result, transport);
 
             // Failed to authenticate
-            sasl->outcome = PN_SASL_AUTH;
-            pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
+            pnx_sasl_fail_authentication(transport);
+            pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
             break;
     }
 }
 
-void pni_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv)
+void cyrus_sasl_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv)
 {
-    pni_sasl_t *sasl = transport->sasl;
-
-    int result = pni_wrap_server_start(sasl, mechanism, recv);
+    int result = pni_wrap_server_start(transport, mechanism, recv);
     if (result==SASL_OK) {
         // We need to filter out a supplied mech in in the inclusion list
         // as the client could have used a mech that we support, but that
         // wasn't on the list we sent.
-        if (!pni_included_mech(sasl->included_mechanisms, pn_bytes(strlen(mechanism), mechanism))) {
-            sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+        if (!pnx_sasl_is_included_mech(transport, pn_bytes(strlen(mechanism), mechanism))) {
+            sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
             sasl_seterror(cyrus_conn, 0, "Client mechanism not in mechanism inclusion list.");
             result = SASL_FAIL;
         }
@@ -428,33 +495,31 @@ void pni_process_init(pn_transport_t *transport, const char *mechanism, const pn
     pni_process_server_result(transport, result);
 }
 
-static int pni_wrap_server_step(pni_sasl_t *sasl, const pn_bytes_t *in)
+static int pni_wrap_server_step(pn_transport_t *transport, const pn_bytes_t *in)
 {
     int result;
     const char *out;
     unsigned outlen;
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context;
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
     result = sasl_server_step(cyrus_conn,
                               in->start, in->size,
                               &out, &outlen);
 
-    sasl->bytes_out.start = out;
-    sasl->bytes_out.size = outlen;
+    pnx_sasl_set_bytes_out(transport, pn_bytes(outlen, out));
     return result;
 }
 
-void pni_process_response(pn_transport_t *transport, const pn_bytes_t *recv)
+void cyrus_sasl_process_response(pn_transport_t *transport, const pn_bytes_t *recv)
 {
-    pni_sasl_t *sasl = transport->sasl;
-    int result = pni_wrap_server_step(sasl, recv);
+    int result = pni_wrap_server_step(transport, recv);
     pni_process_server_result(transport, result);
 }
 
-bool pni_sasl_impl_can_encrypt(pn_transport_t *transport)
+bool cyrus_sasl_can_encrypt(pn_transport_t *transport)
 {
-  if (!transport->sasl->impl_context) return false;
+  sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
+  if (!cyrus_conn) return false;
 
-  sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context;
   // Get SSF to find out if we need to encrypt or not
   const void* value;
   int r = sasl_getprop(cyrus_conn, SASL_SSF, &value);
@@ -469,11 +534,11 @@ bool pni_sasl_impl_can_encrypt(pn_transport_t *transport)
   return false;
 }
 
-ssize_t pni_sasl_impl_max_encrypt_size(pn_transport_t *transport)
+ssize_t cyrus_sasl_max_encrypt_size(pn_transport_t *transport)
 {
-  if (!transport->sasl->impl_context) return PN_ERR;
+  sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
+  if (!cyrus_conn) return PN_ERR;
 
-  sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context;
   const void* value;
   int r = sasl_getprop(cyrus_conn, SASL_MAXOUTBUF, &value);
   if (r != SASL_OK) {
@@ -486,13 +551,13 @@ ssize_t pni_sasl_impl_max_encrypt_size(pn_transport_t *transport)
     // GSSAPI plugin seems to return an incorrect value for the buffer size on the client
     // side, which is greater than the value returned on the server side. Actually using
     // the entire client side buffer will cause a server side error due to a buffer overrun.
-    (transport->sasl->client? 60 : 0);
+    (pnx_sasl_is_client(transport)? 60 : 0);
 }
 
-ssize_t pni_sasl_impl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
+ssize_t cyrus_sasl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
 {
   if ( in.size==0 ) return 0;
-  sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context;
+  sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
   const char *output;
   unsigned int outlen;
   int r = sasl_encode(cyrus_conn, in.start, in.size, &output, &outlen);
@@ -504,10 +569,10 @@ ssize_t pni_sasl_impl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_
   return PN_ERR;
 }
 
-ssize_t pni_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
+ssize_t cyrus_sasl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
 {
   if ( in.size==0 ) return 0;
-  sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context;
+  sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
   const char *output;
   unsigned int outlen;
   int r = sasl_decode(cyrus_conn, in.start, in.size, &output, &outlen);
@@ -519,14 +584,9 @@ ssize_t pni_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_
   return PN_ERR;
 }
 
-void pni_sasl_impl_free(pn_transport_t *transport)
+void cyrus_sasl_free(pn_transport_t *transport)
 {
-    sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context;
+    sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport);
     sasl_dispose(&cyrus_conn);
-    transport->sasl->impl_context = cyrus_conn;
-}
-
-bool pn_sasl_extended(void)
-{
-  return true;
+    pnx_sasl_set_context(transport, cyrus_conn);
 }

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/src/sasl/cyrus_stub.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/cyrus_stub.c b/proton-c/src/sasl/cyrus_stub.c
new file mode 100644
index 0000000..983ace8
--- /dev/null
+++ b/proton-c/src/sasl/cyrus_stub.c
@@ -0,0 +1,40 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "proton/sasl.h"
+#include "proton/sasl-plugin.h"
+
+extern const pnx_sasl_implementation * const cyrus_sasl_impl;
+const pnx_sasl_implementation * const cyrus_sasl_impl = NULL;
+
+bool pn_sasl_extended(void)
+{
+  return false;
+}
+
+void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name)
+{
+}
+
+void pn_sasl_config_path(pn_sasl_t *sasl0, const char *dir)
+{
+}
+

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/src/sasl/default_sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/default_sasl.c b/proton-c/src/sasl/default_sasl.c
new file mode 100644
index 0000000..66dd318
--- /dev/null
+++ b/proton-c/src/sasl/default_sasl.c
@@ -0,0 +1,248 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+#include "proton/sasl.h"
+#include "proton/sasl-plugin.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+// SASL implementation interface
+static void default_sasl_prepare(pn_transport_t *transport);
+static void default_sasl_impl_free(pn_transport_t *transport);
+static const char *default_sasl_impl_list_mechs(pn_transport_t *transport);
+
+static bool default_sasl_init_server(pn_transport_t *transport);
+static void default_sasl_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv);
+static void default_sasl_process_response(pn_transport_t *transport, const pn_bytes_t *recv);
+
+static bool default_sasl_init_client(pn_transport_t *transport);
+static bool default_sasl_process_mechanisms(pn_transport_t *transport, const char *mechs);
+static void default_sasl_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv);
+static void default_sasl_process_outcome(pn_transport_t *transport);
+
+static bool default_sasl_impl_can_encrypt(pn_transport_t *transport);
+static ssize_t default_sasl_impl_max_encrypt_size(pn_transport_t *transport);
+static ssize_t default_sasl_impl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+static ssize_t default_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
+
+extern const pnx_sasl_implementation default_sasl_impl;
+const pnx_sasl_implementation default_sasl_impl = {
+    default_sasl_impl_free,
+    default_sasl_impl_list_mechs,
+
+    default_sasl_init_server,
+    default_sasl_init_client,
+
+    default_sasl_prepare,
+
+    default_sasl_process_init,
+    default_sasl_process_response,
+
+    default_sasl_process_mechanisms,
+    default_sasl_process_challenge,
+    default_sasl_process_outcome,
+
+    default_sasl_impl_can_encrypt,
+    default_sasl_impl_max_encrypt_size,
+    default_sasl_impl_encode,
+    default_sasl_impl_decode
+};
+
+static const char ANONYMOUS[] = "ANONYMOUS";
+static const char EXTERNAL[] = "EXTERNAL";
+static const char PLAIN[] = "PLAIN";
+
+void default_sasl_prepare(pn_transport_t* transport)
+{
+}
+
+bool default_sasl_init_server(pn_transport_t* transport)
+{
+  // Setup to send SASL mechanisms frame
+  pnx_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS);
+  return true;
+}
+
+bool default_sasl_init_client(pn_transport_t* transport)
+{
+  return true;
+}
+
+void default_sasl_impl_free(pn_transport_t *transport)
+{
+  free(pnx_sasl_get_context(transport));
+}
+
+// Client handles ANONYMOUS or PLAIN mechanisms if offered
+bool default_sasl_process_mechanisms(pn_transport_t *transport, const char *mechs)
+{
+  const char *username = pnx_sasl_get_username(transport);
+  const char *password = pnx_sasl_get_password(transport);
+
+  // Check whether offered EXTERNAL, PLAIN or ANONYMOUS
+  // Look for "EXTERNAL" in mechs
+  const char *found = strstr(mechs, EXTERNAL);
+  // Make sure that string is separated and terminated and allowed
+  if (found && (found==mechs || found[-1]==' ') && (found[8]==0 || found[8]==' ') &&
+      pnx_sasl_is_included_mech(transport, pn_bytes(8, found))) {
+    pnx_sasl_set_selected_mechanism(transport, EXTERNAL);
+    if (username) {
+      size_t size = strlen(username);
+      char *iresp = (char *) malloc(size);
+      if (!iresp) return false;
+
+      pnx_sasl_set_context(transport, iresp);
+
+      memmove(iresp, username, size);
+      pnx_sasl_set_bytes_out(transport, pn_bytes(size, iresp));
+    } else {
+      static const char empty[] = "";
+      pnx_sasl_set_bytes_out(transport, pn_bytes(0, empty));
+    }
+    pnx_sasl_set_desired_state(transport, SASL_POSTED_INIT);
+    return true;
+  }
+
+  // Look for "PLAIN" in mechs
+  found = strstr(mechs, PLAIN);
+  // Make sure that string is separated and terminated, allowed
+  // and we have a username and password and connection is encrypted or we allow insecure
+  if (found && (found==mechs || found[-1]==' ') && (found[5]==0 || found[5]==' ') &&
+      pnx_sasl_is_included_mech(transport, pn_bytes(5, found)) &&
+      (pnx_sasl_is_transport_encrypted(transport) || pnx_sasl_get_allow_insecure_mechs(transport)) &&
+      username && password) {
+    pnx_sasl_set_selected_mechanism(transport, PLAIN);
+    size_t usize = strlen(username);
+    size_t psize = strlen(password);
+    size_t size = usize + psize + 2;
+    char *iresp = (char *) malloc(size);
+    if (!iresp) return false;
+
+    pnx_sasl_set_context(transport, iresp);
+
+    iresp[0] = 0;
+    memmove(&iresp[1], username, usize);
+    iresp[usize + 1] = 0;
+    memmove(&iresp[usize + 2], password, psize);
+    pnx_sasl_set_bytes_out(transport, pn_bytes(size, iresp));
+
+    // Zero out password and dealloc
+    pnx_sasl_clear_password(transport);
+
+    pnx_sasl_set_desired_state(transport, SASL_POSTED_INIT);
+    return true;
+  }
+
+  // Look for "ANONYMOUS" in mechs
+  found = strstr(mechs, ANONYMOUS);
+  // Make sure that string is separated and terminated and allowed
+  if (found && (found==mechs || found[-1]==' ') && (found[9]==0 || found[9]==' ') &&
+      pnx_sasl_is_included_mech(transport, pn_bytes(9, found))) {
+    pnx_sasl_set_selected_mechanism(transport, ANONYMOUS);
+    if (username) {
+      size_t size = strlen(username);
+      char *iresp = (char *) malloc(size);
+      if (!iresp) return false;
+
+      pnx_sasl_set_context(transport, iresp);
+
+      memmove(iresp, username, size);
+      pnx_sasl_set_bytes_out(transport, pn_bytes(size, iresp));
+    } else {
+      static const char anon[] = "anonymous";
+      pnx_sasl_set_bytes_out(transport, pn_bytes(sizeof anon-1, anon));
+    }
+    pnx_sasl_set_desired_state(transport, SASL_POSTED_INIT);
+    return true;
+  }
+  return false;
+}
+
+// Server will offer only ANONYMOUS and EXTERNAL if appropriate
+const char *default_sasl_impl_list_mechs(pn_transport_t *transport)
+{
+  // If we have an external authid then we can offer EXTERNAL
+  if (pnx_sasl_get_external_username(transport)) {
+    return "EXTERNAL ANONYMOUS";
+  } else {
+    return "ANONYMOUS";
+  }
+}
+
+void default_sasl_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv)
+{
+  // Check that mechanism is ANONYMOUS and it is allowed
+  if (strcmp(mechanism, ANONYMOUS)==0 &&
+      pnx_sasl_is_included_mech(transport, pn_bytes(sizeof(ANONYMOUS)-1, ANONYMOUS))) {
+    pnx_sasl_succeed_authentication(transport, "anonymous");
+    pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
+    return;
+  }
+
+  // Or maybe EXTERNAL
+  const char *ext_username = pnx_sasl_get_external_username(transport);
+  if (strcmp(mechanism, EXTERNAL)==0 &&
+      pnx_sasl_is_included_mech(transport, pn_bytes(sizeof(EXTERNAL)-1, EXTERNAL)) &&
+      ext_username) {
+    pnx_sasl_succeed_authentication(transport, ext_username);
+    pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
+    return;
+  }
+
+  // Otherwise authentication failed
+  pnx_sasl_fail_authentication(transport);
+  pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
+
+}
+
+/* The default implementation neither sends nor receives challenges or responses */
+void default_sasl_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv)
+{
+}
+
+void default_sasl_process_response(pn_transport_t *transport, const pn_bytes_t *recv)
+{
+}
+
+void default_sasl_process_outcome(pn_transport_t* transport)
+{
+}
+
+bool default_sasl_impl_can_encrypt(pn_transport_t *transport)
+{
+  return false;
+}
+
+ssize_t default_sasl_impl_max_encrypt_size(pn_transport_t *transport)
+{
+  return 0;
+}
+
+ssize_t default_sasl_impl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
+{
+  return 0;
+}
+
+ssize_t default_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
+{
+  return 0;
+}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/src/sasl/none_sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/none_sasl.c b/proton-c/src/sasl/none_sasl.c
deleted file mode 100644
index 0408a8f..0000000
--- a/proton-c/src/sasl/none_sasl.c
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
- */
-
-#include "sasl-internal.h"
-
-#include "core/engine-internal.h"
-
-static const char ANONYMOUS[] = "ANONYMOUS";
-static const char EXTERNAL[] = "EXTERNAL";
-static const char PLAIN[] = "PLAIN";
-
-bool pni_init_server(pn_transport_t* transport)
-{
-  return true;
-}
-
-bool pni_init_client(pn_transport_t* transport)
-{
-  return true;
-}
-
-void pni_sasl_impl_free(pn_transport_t *transport)
-{
-  free(transport->sasl->impl_context);
-}
-
-// Client handles ANONYMOUS or PLAIN mechanisms if offered
-bool pni_process_mechanisms(pn_transport_t *transport, const char *mechs)
-{
-  // Check whether offered EXTERNAL, PLAIN or ANONYMOUS
-  // Look for "EXTERNAL" in mechs
-  const char *found = strstr(mechs, EXTERNAL);
-  // Make sure that string is separated and terminated and allowed
-  if (found && (found==mechs || found[-1]==' ') && (found[8]==0 || found[8]==' ') &&
-      pni_included_mech(transport->sasl->included_mechanisms, pn_bytes(8, found))) {
-    transport->sasl->selected_mechanism = pn_strdup(EXTERNAL);
-    if (transport->sasl->username) {
-      size_t size = strlen(transport->sasl->username);
-      char *iresp = (char *) malloc(size);
-      if (!iresp) return false;
-
-      transport->sasl->impl_context = iresp;
-
-      memmove(iresp, transport->sasl->username, size);
-      transport->sasl->bytes_out.start = iresp;
-      transport->sasl->bytes_out.size =  size;
-    } else {
-      static const char empty[] = "";
-      transport->sasl->bytes_out.start = empty;
-      transport->sasl->bytes_out.size =  0;
-    }
-    return true;
-  }
-
-  // Look for "PLAIN" in mechs
-  found = strstr(mechs, PLAIN);
-  // Make sure that string is separated and terminated, allowed
-  // and we have a username and password and connection is encrypted or we allow insecure
-  if (found && (found==mechs || found[-1]==' ') && (found[5]==0 || found[5]==' ') &&
-      pni_included_mech(transport->sasl->included_mechanisms, pn_bytes(5, found)) &&
-      (transport->sasl->external_ssf > 0 || transport->sasl->allow_insecure_mechs) &&
-      transport->sasl->username && transport->sasl->password) {
-    transport->sasl->selected_mechanism = pn_strdup(PLAIN);
-    size_t usize = strlen(transport->sasl->username);
-    size_t psize = strlen(transport->sasl->password);
-    size_t size = usize + psize + 2;
-    char *iresp = (char *) malloc(size);
-    if (!iresp) return false;
-
-    transport->sasl->impl_context = iresp;
-
-    iresp[0] = 0;
-    memmove(&iresp[1], transport->sasl->username, usize);
-    iresp[usize + 1] = 0;
-    memmove(&iresp[usize + 2], transport->sasl->password, psize);
-    transport->sasl->bytes_out.start = iresp;
-    transport->sasl->bytes_out.size =  size;
-
-    // Zero out password and dealloc
-    free(memset(transport->sasl->password, 0, psize));
-    transport->sasl->password = NULL;
-
-    return true;
-  }
-
-  // Look for "ANONYMOUS" in mechs
-  found = strstr(mechs, ANONYMOUS);
-  // Make sure that string is separated and terminated and allowed
-  if (found && (found==mechs || found[-1]==' ') && (found[9]==0 || found[9]==' ') &&
-      pni_included_mech(transport->sasl->included_mechanisms, pn_bytes(9, found))) {
-    transport->sasl->selected_mechanism = pn_strdup(ANONYMOUS);
-    if (transport->sasl->username) {
-      size_t size = strlen(transport->sasl->username);
-      char *iresp = (char *) malloc(size);
-      if (!iresp) return false;
-
-      transport->sasl->impl_context = iresp;
-
-      memmove(iresp, transport->sasl->username, size);
-      transport->sasl->bytes_out.start = iresp;
-      transport->sasl->bytes_out.size =  size;
-    } else {
-      static const char anon[] = "anonymous";
-      transport->sasl->bytes_out.start = anon;
-      transport->sasl->bytes_out.size =  sizeof anon-1;
-    }
-    return true;
-  }
-  return false;
-}
-
-// Server will offer only ANONYMOUS and EXTERNAL if appropriate
-int pni_sasl_impl_list_mechs(pn_transport_t *transport, char **mechlist)
-{
-  // If we have an external authid then we can offer EXTERNAL
-  if (transport->sasl && transport->sasl->external_auth) {
-    *mechlist = pn_strdup("EXTERNAL ANONYMOUS");
-    return 2;
-  } else {
-    *mechlist = pn_strdup("ANONYMOUS");
-    return 1;
-  }
-}
-
-void pni_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv)
-{
-  // Check that mechanism is ANONYMOUS and it is allowed
-  if (strcmp(mechanism, ANONYMOUS)==0 &&
-      pni_included_mech(transport->sasl->included_mechanisms, pn_bytes(sizeof(ANONYMOUS)-1, ANONYMOUS))) {
-    transport->sasl->username = "anonymous";
-    transport->sasl->outcome = PN_SASL_OK;
-    transport->authenticated = true;
-    pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
-  } else if (strcmp(mechanism, EXTERNAL)==0 &&
-      transport->sasl->external_auth &&
-      pni_included_mech(transport->sasl->included_mechanisms, pn_bytes(sizeof(EXTERNAL)-1, EXTERNAL))) {
-    transport->sasl->username = transport->sasl->external_auth;
-    transport->sasl->outcome = PN_SASL_OK;
-    transport->authenticated = true;
-    pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
-  } else {
-    transport->sasl->outcome = PN_SASL_AUTH;
-    pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME);
-  }
-}
-
-/* The default implementation neither sends nor receives challenges or responses */
-void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv)
-{
-}
-
-void pni_process_response(pn_transport_t *transport, const pn_bytes_t *recv)
-{
-}
-
-bool pni_sasl_impl_can_encrypt(pn_transport_t *transport)
-{
-  return false;
-}
-
-ssize_t pni_sasl_impl_max_encrypt_size(pn_transport_t *transport)
-{
-  return 0;
-}
-
-ssize_t pni_sasl_impl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
-{
-  return 0;
-}
-
-ssize_t pni_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
-{
-  return 0;
-}
-
-bool pn_sasl_extended(void)
-{
-  return false;
-}

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/src/sasl/sasl-internal.h
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/sasl-internal.h b/proton-c/src/sasl/sasl-internal.h
index 2873777..fc141b4 100644
--- a/proton-c/src/sasl/sasl-internal.h
+++ b/proton-c/src/sasl/sasl-internal.h
@@ -27,7 +27,10 @@
 
 #include "proton/types.h"
 #include "proton/sasl.h"
+#include "proton/sasl-plugin.h"
 
+extern const pnx_sasl_implementation default_sasl_impl;
+extern const pnx_sasl_implementation * const cyrus_sasl_impl;
 
 // SASL APIs used by transport code
 void pn_sasl_free(pn_transport_t *transport);
@@ -35,45 +38,13 @@ void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, con
 void pni_sasl_set_remote_hostname(pn_transport_t *transport, const char* fqdn);
 void pni_sasl_set_external_security(pn_transport_t *transport, int ssf, const char *authid);
 
-// Internal SASL authenticator interface
-void pni_sasl_impl_free(pn_transport_t *transport);
-int  pni_sasl_impl_list_mechs(pn_transport_t *transport, char **mechlist);
-bool pni_init_server(pn_transport_t *transport);
-void pni_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv);
-void pni_process_response(pn_transport_t *transport, const pn_bytes_t *recv);
-
-bool pni_init_client(pn_transport_t *transport);
-bool pni_process_mechanisms(pn_transport_t *transport, const char *mechs);
-void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv);
-
-// Internal SASL security layer interface
-bool pni_sasl_impl_can_encrypt(pn_transport_t *transport);
-ssize_t pni_sasl_impl_max_encrypt_size(pn_transport_t *transport);
-ssize_t pni_sasl_impl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
-ssize_t pni_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out);
-
-
-// Shared SASL API used by the actual SASL authenticators
-enum pni_sasl_state {
-  SASL_NONE,
-  SASL_POSTED_INIT,
-  SASL_POSTED_MECHANISMS,
-  SASL_POSTED_RESPONSE,
-  SASL_POSTED_CHALLENGE,
-  SASL_RECVED_OUTCOME_SUCCEED,
-  SASL_RECVED_OUTCOME_FAIL,
-  SASL_POSTED_OUTCOME,
-  SASL_ERROR
-};
-
 struct pni_sasl_t {
   void *impl_context;
+  const pnx_sasl_implementation* impl;
   char *selected_mechanism;
   char *included_mechanisms;
   const char *username;
   char *password;
-  char *config_name;
-  char *config_dir;
   const char *remote_fqdn;
   char *external_auth;
   int external_ssf;
@@ -82,15 +53,10 @@ struct pni_sasl_t {
   pn_buffer_t* encoded_buffer;
   pn_bytes_t bytes_out;
   pn_sasl_outcome_t outcome;
-  enum pni_sasl_state desired_state;
-  enum pni_sasl_state last_state;
+  enum pnx_sasl_state desired_state;
+  enum pnx_sasl_state last_state;
   bool allow_insecure_mechs;
   bool client;
 };
 
-// Shared Utility used by sasl implementations
-void pni_split_mechs(char *mechlist, const char* included_mechs, char *mechs[], int *count);
-bool pni_included_mech(const char *included_mech_list, pn_bytes_t s);
-void pni_sasl_set_desired_state(pn_transport_t *transport, enum pni_sasl_state desired_state);
-
 #endif /* sasl-internal.h */

http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/21a74d98/proton-c/src/sasl/sasl.c
----------------------------------------------------------------------
diff --git a/proton-c/src/sasl/sasl.c b/proton-c/src/sasl/sasl.c
index 55c489d..a39e602 100644
--- a/proton-c/src/sasl/sasl.c
+++ b/proton-c/src/sasl/sasl.c
@@ -32,6 +32,209 @@
 
 #include <assert.h>
 
+// Machinery to allow plugin SASL implementations
+// Change this to &default_sasl_impl when we change cyrus to opt in
+static const pnx_sasl_implementation *global_sasl_impl = NULL;
+
+//-----------------------------------------------------------------------------
+// pnx_sasl: API for SASL implementations to use
+
+void pnx_sasl_logf(pn_transport_t *logger, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    if (logger->trace & PN_TRACE_DRV)
+        pn_transport_vlogf(logger, fmt, ap);
+    va_end(ap);
+}
+
+void *pnx_sasl_get_context(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->impl_context : NULL;
+}
+
+void  pnx_sasl_set_context(pn_transport_t *transport, void *context)
+{
+  if (transport->sasl) transport->sasl->impl_context = context;
+}
+
+bool  pnx_sasl_is_client(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->client : false;
+}
+
+bool  pnx_sasl_is_transport_encrypted(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->external_ssf>0 : false;
+}
+
+bool  pnx_sasl_get_allow_insecure_mechs(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->allow_insecure_mechs : false;
+}
+
+bool  pnx_sasl_get_auth_required(pn_transport_t *transport)
+{
+  return transport->auth_required;
+}
+
+const char *pnx_sasl_get_username(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->username : NULL;
+}
+
+const char *pnx_sasl_get_external_username(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->external_auth : NULL;
+}
+
+int   pnx_sasl_get_external_ssf(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->external_ssf : 0;
+}
+
+const char *pnx_sasl_get_password(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->password : NULL;
+}
+
+void  pnx_sasl_clear_password(pn_transport_t *transport)
+{
+  if (transport->sasl) {
+    char *password = transport->sasl->password;
+    free(memset(password, 0, strlen(password)));
+    transport->sasl->password = NULL;
+  }
+}
+
+const char *pnx_sasl_get_remote_fqdn(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->remote_fqdn : NULL;
+}
+
+const char *pnx_sasl_get_selected_mechanism(pn_transport_t *transport)
+{
+  return transport->sasl ? transport->sasl->selected_mechanism : NULL;
+}
+
+void  pnx_sasl_set_bytes_out(pn_transport_t *transport, pn_bytes_t bytes)
+{
+  if (transport->sasl) {
+    transport->sasl->bytes_out = bytes;
+  }
+}
+
+void  pnx_sasl_set_selected_mechanism(pn_transport_t *transport, const char *mechanism)
+{
+  if (transport->sasl) {
+    transport->sasl->selected_mechanism = pn_strdup(mechanism);
+  }
+}
+
+void  pnx_sasl_succeed_authentication(pn_transport_t *transport, const char *username)
+{
+  if (transport->sasl) {
+    transport->sasl->username = username;
+    transport->sasl->outcome = PN_SASL_OK;
+    transport->authenticated = true;
+  }
+}
+
+void  pnx_sasl_fail_authentication(pn_transport_t *transport)
+{
+  if (transport->sasl) {
+    transport->sasl->outcome = PN_SASL_AUTH;
+  }
+}
+
+void pnx_sasl_set_implementation(pn_transport_t *transport, const pnx_sasl_implementation *i, void* context)
+{
+  transport->sasl->impl = i;
+  transport->sasl->impl_context = context;
+}
+
+void pnx_sasl_set_default_implementation(const pnx_sasl_implementation* impl)
+{
+  global_sasl_impl = impl;
+}
+
+
+//-----------------------------------------------------------------------------
+// pni_sasl_impl: Abstract the entry points to the SASL implementation (virtual function calls)
+
+static inline void pni_sasl_impl_free(pn_transport_t *transport)
+{
+  transport->sasl->impl->free(transport);
+}
+
+static inline const char *pni_sasl_impl_list_mechs(pn_transport_t *transport)
+{
+  return transport->sasl->impl->list_mechs(transport);
+}
+
+static inline bool pni_sasl_impl_init_server(pn_transport_t *transport)
+{
+  return transport->sasl->impl->init_server(transport);
+}
+
+static inline void pni_sasl_impl_prepare_write(pn_transport_t *transport)
+{
+  transport->sasl->impl->prepare_write(transport);
+}
+
+static inline void pni_sasl_impl_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv)
+{
+  transport->sasl->impl->process_init(transport, mechanism, recv);
+}
+
+static inline void pni_sasl_impl_process_response(pn_transport_t *transport, const pn_bytes_t *recv)
+{
+  transport->sasl->impl->process_response(transport, recv);
+}
+
+static inline bool pni_sasl_impl_init_client(pn_transport_t *transport)
+{
+  return transport->sasl->impl->init_client(transport);
+}
+
+static inline bool pni_sasl_impl_process_mechanisms(pn_transport_t *transport, const char *mechs)
+{
+  return transport->sasl->impl->process_mechanisms(transport, mechs);
+}
+
+static inline void pni_sasl_impl_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv)
+{
+  transport->sasl->impl->process_challenge(transport, recv);
+}
+
+static inline void pni_sasl_impl_process_outcome(pn_transport_t *transport)
+{
+  transport->sasl->impl->process_outcome(transport);
+}
+
+static inline bool pni_sasl_impl_can_encrypt(pn_transport_t *transport)
+{
+    return transport->sasl->impl->can_encrypt(transport);
+}
+
+static inline ssize_t pni_sasl_impl_max_encrypt_size(pn_transport_t *transport)
+{
+    return transport->sasl->impl->max_encrypt_size(transport);
+}
+
+static inline ssize_t pni_sasl_impl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
+{
+    return transport->sasl->impl->encode(transport, in, out);
+}
+
+static inline ssize_t pni_sasl_impl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out)
+{
+    return transport->sasl->impl->decode(transport, in, out);
+}
+
+//-----------------------------------------------------------------------------
+// General SASL implementation
+
 static inline pni_sasl_t *get_sasl_internal(pn_sasl_t *sasl)
 {
   // The external pn_sasl_t is really a pointer to the internal pni_transport_t
@@ -89,7 +292,7 @@ const pn_io_layer_t sasl_encrypt_layer = {
 #define SASL_HEADER ("AMQP\x03\x01\x00\x00")
 #define SASL_HEADER_LEN 8
 
-static bool pni_sasl_is_server_state(enum pni_sasl_state state)
+static bool pni_sasl_is_server_state(enum pnx_sasl_state state)
 {
   return state==SASL_NONE
       || state==SASL_POSTED_MECHANISMS
@@ -98,7 +301,7 @@ static bool pni_sasl_is_server_state(enum pni_sasl_state state)
       || state==SASL_ERROR;
 }
 
-static bool pni_sasl_is_client_state(enum pni_sasl_state state)
+static bool pni_sasl_is_client_state(enum pnx_sasl_state state)
 {
   return state==SASL_NONE
       || state==SASL_POSTED_INIT
@@ -110,7 +313,7 @@ static bool pni_sasl_is_client_state(enum pni_sasl_state state)
 
 static bool pni_sasl_is_final_input_state(pni_sasl_t *sasl)
 {
-  enum pni_sasl_state desired_state = sasl->desired_state;
+  enum pnx_sasl_state desired_state = sasl->desired_state;
   return desired_state==SASL_RECVED_OUTCOME_SUCCEED
       || desired_state==SASL_RECVED_OUTCOME_FAIL
       || desired_state==SASL_ERROR
@@ -119,8 +322,8 @@ static bool pni_sasl_is_final_input_state(pni_sasl_t *sasl)
 
 static bool pni_sasl_is_final_output_state(pni_sasl_t *sasl)
 {
-  enum pni_sasl_state last_state = sasl->last_state;
-  enum pni_sasl_state desired_state = sasl->desired_state;
+  enum pnx_sasl_state last_state = sasl->last_state;
+  enum pnx_sasl_state desired_state = sasl->desired_state;
   return (desired_state==SASL_RECVED_OUTCOME_SUCCEED && last_state>=SASL_POSTED_INIT)
       || last_state==SASL_RECVED_OUTCOME_SUCCEED
       || last_state==SASL_RECVED_OUTCOME_FAIL
@@ -136,7 +339,7 @@ static void pni_emit(pn_transport_t *transport)
   }
 }
 
-void pni_sasl_set_desired_state(pn_transport_t *transport, enum pni_sasl_state desired_state)
+void pnx_sasl_set_desired_state(pn_transport_t *transport, enum pnx_sasl_state desired_state)
 {
   pni_sasl_t *sasl = transport->sasl;
   if (sasl->last_state > desired_state) {
@@ -157,9 +360,73 @@ void pni_sasl_set_desired_state(pn_transport_t *transport, enum pni_sasl_state d
     if (sasl->last_state==desired_state && desired_state==SASL_POSTED_CHALLENGE) {
       sasl->last_state = SASL_POSTED_MECHANISMS;
     }
+    bool changed = sasl->desired_state != desired_state;
     sasl->desired_state = desired_state;
     // Don't emit transport event on error as there will be a TRANSPORT_ERROR event
-    if (desired_state != SASL_ERROR) pni_emit(transport);
+    if (desired_state != SASL_ERROR && changed) pni_emit(transport);
+  }
+}
+
+// Look for symbol in the mech include list - not particlarly efficient,
+// but probably not used enough to matter.
+//
+// Note that if there is no inclusion list then every mech is implicitly included.
+static bool pni_sasl_included_mech(const char *included_mech_list, pn_bytes_t s)
+{
+  if (!included_mech_list) return true;
+
+  const char * end_list = included_mech_list+strlen(included_mech_list);
+  size_t len = s.size;
+  const char *c = included_mech_list;
+  while (c!=NULL) {
+    // If there are not enough chars left in the list no matches
+    if ((ptrdiff_t)len > end_list-c) return false;
+
+    // Is word equal with a space or end of string afterwards?
+    if (pn_strncasecmp(c, s.start, len)==0 && (c[len]==' ' || c[len]==0) ) return true;
+
+    c = strchr(c, ' ');
+    c = c ? c+1 : NULL;
+  }
+  return false;
+}
+
+// Look for symbol in the mech include list - plugin API version
+//
+// Note that if there is no inclusion list then every mech is implicitly included.
+bool pnx_sasl_is_included_mech(pn_transport_t* transport, pn_bytes_t s)
+{
+  return pni_sasl_included_mech(transport->sasl->included_mechanisms, s);
+}
+
+// This takes a space separated list and zero terminates it in place
+// whilst adding pointers to the existing strings in a string array.
+// This means that you can't free the original storage until you have
+// finished with the resulting list.
+static void pni_split_mechs(char *mechlist, const char* included_mechs, char *mechs[], int *count)
+{
+  char *start = mechlist;
+  char *end = start;
+
+  while (*end) {
+    if (*end == ' ') {
+      if (start != end) {
+        *end = '\0';
+        if (pni_sasl_included_mech(included_mechs, pn_bytes(end-start, start))) {
+          mechs[(*count)++] = start;
+        }
+      }
+      end++;
+      start = end;
+    } else {
+      end++;
+    }
+  }
+
+  if (start != end) {
+    if (pni_sasl_included_mech(included_mechs, pn_bytes(end-start, start))) {
+      mechs[(*count)++] = start;
+    }
   }
 }
 
@@ -168,7 +435,7 @@ static void pni_post_sasl_frame(pn_transport_t *transport)
 {
   pni_sasl_t *sasl = transport->sasl;
   pn_bytes_t out = sasl->bytes_out;
-  enum pni_sasl_state desired_state = sasl->desired_state;
+  enum pnx_sasl_state desired_state = sasl->desired_state;
   while (sasl->desired_state > sasl->last_state) {
     switch (desired_state) {
     case SASL_POSTED_INIT:
@@ -179,10 +446,10 @@ static void pni_post_sasl_frame(pn_transport_t *transport)
     case SASL_POSTED_MECHANISMS: {
       // TODO: Hardcoded limit of 16 mechanisms
       char *mechs[16];
-      char *mechlist = NULL;
+      char *mechlist = pn_strdup(pni_sasl_impl_list_mechs(transport));
 
       int count = 0;
-      if (pni_sasl_impl_list_mechs(transport, &mechlist) > 0) {
+      if (mechlist) {
         pni_split_mechs(mechlist, sasl->included_mechanisms, mechs, &count);
       }
 
@@ -192,16 +459,19 @@ static void pni_post_sasl_frame(pn_transport_t *transport)
       break;
     }
     case SASL_POSTED_RESPONSE:
-      pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", SASL_RESPONSE, out.size, out.start);
-      pni_emit(transport);
+      if (sasl->last_state != SASL_POSTED_RESPONSE) {
+        pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", SASL_RESPONSE, out.size, out.start);
+        pni_emit(transport);
+      }
       break;
     case SASL_POSTED_CHALLENGE:
       if (sasl->last_state < SASL_POSTED_MECHANISMS) {
         desired_state = SASL_POSTED_MECHANISMS;
         continue;
+      } else if (sasl->last_state != SASL_POSTED_CHALLENGE) {
+        pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", SASL_CHALLENGE, out.size, out.start);
+        pni_emit(transport);
       }
-      pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", SASL_CHALLENGE, out.size, out.start);
-      pni_emit(transport);
       break;
     case SASL_POSTED_OUTCOME:
       if (sasl->last_state < SASL_POSTED_MECHANISMS) {
@@ -212,7 +482,7 @@ static void pni_post_sasl_frame(pn_transport_t *transport)
       pni_emit(transport);
       if (sasl->outcome!=PN_SASL_OK) {
         pn_do_error(transport, "amqp:unauthorized-access", "Failed to authenticate client [mech=%s]",
-    transport->sasl->selected_mechanism ? transport->sasl->selected_mechanism : "none");
+                    transport->sasl->selected_mechanism ? transport->sasl->selected_mechanism : "none");
         desired_state = SASL_ERROR;
       }
       break;
@@ -224,7 +494,7 @@ static void pni_post_sasl_frame(pn_transport_t *transport)
       break;
     case SASL_RECVED_OUTCOME_FAIL:
       pn_do_error(transport, "amqp:unauthorized-access", "Authentication failed [mech=%s]",
-  transport->sasl->selected_mechanism ? transport->sasl->selected_mechanism : "none");
+                  transport->sasl->selected_mechanism ? transport->sasl->selected_mechanism : "none");
       desired_state = SASL_ERROR;
       break;
     case SASL_ERROR:
@@ -240,7 +510,7 @@ static void pni_post_sasl_frame(pn_transport_t *transport)
 static void pn_error_sasl(pn_transport_t* transport, unsigned int layer)
 {
   transport->close_sent = true;
-  pni_sasl_set_desired_state(transport, SASL_ERROR);
+  pnx_sasl_set_desired_state(transport, SASL_ERROR);
 }
 
 static ssize_t pn_input_read_sasl_header(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available)
@@ -277,10 +547,7 @@ static void pni_sasl_start_server_if_needed(pn_transport_t *transport)
 {
   pni_sasl_t *sasl = transport->sasl;
   if (!sasl->client && sasl->desired_state<SASL_POSTED_MECHANISMS) {
-    if (!pni_init_server(transport)) return;
-
-    // Setup to send SASL mechanisms frame
-    pni_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS);
+    pni_sasl_impl_init_server(transport);
   }
 }
 
@@ -366,6 +633,8 @@ static ssize_t pn_output_write_sasl(pn_transport_t* transport, unsigned int laye
 
   pni_sasl_start_server_if_needed(transport);
 
+  pni_sasl_impl_prepare_write(transport);
+
   pni_post_sasl_frame(transport);
 
   if (transport->available != 0 || !pni_sasl_is_final_output_state(sasl)) {
@@ -416,81 +685,23 @@ static ssize_t pn_output_write_sasl_encrypt(pn_transport_t* transport, unsigned
   return size;
 }
 
-// Look for symbol in the mech include list - not particlarly efficient,
-// but probably not used enough to matter.
-//
-// Note that if there is no inclusion list then every mech is implicitly included.
-bool pni_included_mech(const char *included_mech_list, pn_bytes_t s)
-{
-  if (!included_mech_list) return true;
-
-  const char * end_list = included_mech_list+strlen(included_mech_list);
-  size_t len = s.size;
-  const char *c = included_mech_list;
-  while (c!=NULL) {
-    // If there are not enough chars left in the list no matches
-    if ((ptrdiff_t)len > end_list-c) return false;
-
-    // Is word equal with a space or end of string afterwards?
-    if (pn_strncasecmp(c, s.start, len)==0 && (c[len]==' ' || c[len]==0) ) return true;
-
-    c = strchr(c, ' ');
-    c = c ? c+1 : NULL;
-  }
-  return false;
-}
-
-// This takes a space separated list and zero terminates it in place
-// whilst adding pointers to the existing strings in a string array.
-// This means that you can't free the original storage until you have
-// finished with the resulting list.
-void pni_split_mechs(char *mechlist, const char* included_mechs, char *mechs[], int *count)
-{
-  char *start = mechlist;
-  char *end = start;
-
-  while (*end) {
-    if (*end == ' ') {
-      if (start != end) {
-        *end = '\0';
-        if (pni_included_mech(included_mechs, pn_bytes(end-start, start))) {
-          mechs[(*count)++] = start;
-        }
-      }
-      end++;
-      start = end;
-    } else {
-      end++;
-    }
-  }
-
-  if (start != end) {
-    if (pni_included_mech(included_mechs, pn_bytes(end-start, start))) {
-      mechs[(*count)++] = start;
-    }
-  }
-}
-
 pn_sasl_t *pn_sasl(pn_transport_t *transport)
 {
   if (!transport->sasl) {
     pni_sasl_t *sasl = (pni_sasl_t *) malloc(sizeof(pni_sasl_t));
 
-    const char *sasl_config_path = getenv("PN_SASL_CONFIG_PATH");
-
     sasl->impl_context = NULL;
+    // Change this to just global_sasl_impl when we make cyrus opt in
+    sasl->impl = global_sasl_impl ? global_sasl_impl : cyrus_sasl_impl ? cyrus_sasl_impl : &default_sasl_impl;
     sasl->client = !transport->server;
     sasl->selected_mechanism = NULL;
     sasl->included_mechanisms = NULL;
     sasl->username = NULL;
     sasl->password = NULL;
-    sasl->config_name = NULL;
-    sasl->config_dir =  sasl_config_path ? pn_strdup(sasl_config_path) : NULL;
     sasl->remote_fqdn = NULL;
     sasl->external_auth = NULL;
     sasl->external_ssf = 0;
     sasl->outcome = PN_SASL_NONE;
-    sasl->impl_context = NULL;
     sasl->decoded_buffer = pn_buffer(0);
     sasl->encoded_buffer = pn_buffer(0);
     sasl->bytes_out.size = 0;
@@ -514,13 +725,10 @@ void pn_sasl_free(pn_transport_t *transport)
       free(sasl->selected_mechanism);
       free(sasl->included_mechanisms);
       free(sasl->password);
-      free(sasl->config_name);
-      free(sasl->config_dir);
       free(sasl->external_auth);
 
-      // CYRUS_SASL
       if (sasl->impl_context) {
-          pni_sasl_impl_free(transport);
+        pni_sasl_impl_free(transport);
       }
       pn_buffer_free(sasl->decoded_buffer);
       pn_buffer_free(sasl->encoded_buffer);
@@ -582,20 +790,6 @@ bool pn_sasl_get_allow_insecure_mechs(pn_sasl_t *sasl0)
     return sasl->allow_insecure_mechs;
 }
 
-void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name)
-{
-    pni_sasl_t *sasl = get_sasl_internal(sasl0);
-    free(sasl->config_name);
-    sasl->config_name = pn_strdup(name);
-}
-
-void pn_sasl_config_path(pn_sasl_t *sasl0, const char *dir)
-{
-    pni_sasl_t *sasl = get_sasl_internal(sasl0);
-    free(sasl->config_dir);
-    sasl->config_dir = pn_strdup(dir);
-}
-
 void pn_sasl_done(pn_sasl_t *sasl0, pn_sasl_outcome_t outcome)
 {
   pni_sasl_t *sasl = get_sasl_internal(sasl0);
@@ -620,7 +814,7 @@ int pn_do_init(pn_transport_t *transport, uint8_t frame_type, uint16_t channel,
   if (err) return err;
   sasl->selected_mechanism = pn_strndup(mech.start, mech.size);
 
-  pni_process_init(transport, sasl->selected_mechanism, &recv);
+  pni_sasl_impl_process_init(transport, sasl->selected_mechanism, &recv);
 
   return 0;
 }
@@ -643,7 +837,7 @@ int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t cha
     // Now keep checking for end of array and pull a symbol
     while(pn_data_next(args)) {
       pn_bytes_t s = pn_data_get_symbol(args);
-      if (pni_included_mech(transport->sasl->included_mechanisms, s)) {
+      if (pnx_sasl_is_included_mech(transport, s)) {
         pn_string_addf(mechs, "%*s ", (int)s.size, s.start);
       }
     }
@@ -661,13 +855,11 @@ int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t cha
     pn_string_setn(mechs, symbol.start, symbol.size);
   }
 
-  if (pni_init_client(transport) &&
-      pn_string_size(mechs) &&
-      pni_process_mechanisms(transport, pn_string_get(mechs))) {
-    pni_sasl_set_desired_state(transport, SASL_POSTED_INIT);
-  } else {
+  if (!(pni_sasl_impl_init_client(transport) &&
+        pn_string_size(mechs) &&
+        pni_sasl_impl_process_mechanisms(transport, pn_string_get(mechs)))) {
     sasl->outcome = PN_SASL_PERM;
-    pni_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL);
+    pnx_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL);
   }
 
   pn_free(mechs);
@@ -681,7 +873,7 @@ int pn_do_challenge(pn_transport_t *transport, uint8_t frame_type, uint16_t chan
   int err = pn_data_scan(args, "D.[z]", &recv);
   if (err) return err;
 
-  pni_process_challenge(transport, &recv);
+  pni_sasl_impl_process_challenge(transport, &recv);
 
   return 0;
 }
@@ -693,7 +885,7 @@ int pn_do_response(pn_transport_t *transport, uint8_t frame_type, uint16_t chann
   int err = pn_data_scan(args, "D.[z]", &recv);
   if (err) return err;
 
-  pni_process_response(transport, &recv);
+  pni_sasl_impl_process_response(transport, &recv);
 
   return 0;
 }
@@ -709,9 +901,10 @@ int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channe
   sasl->outcome = (pn_sasl_outcome_t) outcome;
   bool authenticated = sasl->outcome==PN_SASL_OK;
   transport->authenticated = authenticated;
-  pni_sasl_set_desired_state(transport, authenticated ? SASL_RECVED_OUTCOME_SUCCEED : SASL_RECVED_OUTCOME_FAIL);
+  pnx_sasl_set_desired_state(transport, authenticated ? SASL_RECVED_OUTCOME_SUCCEED : SASL_RECVED_OUTCOME_FAIL);
+
+  pni_sasl_impl_process_outcome(transport);
 
   return 0;
 }
 
-


---------------------------------------------------------------------
To unsubscribe, e-mail: [hidden email]
For additional commands, e-mail: [hidden email]