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 <[email protected]> Authored: Wed Jun 7 01:42:28 2017 -0400 Committer: Andrew Stitcher <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
