PROTON-868: Finished off default implementation of SASL - Client ANONYMOUS and PLAIN - Server ANONYMOUS only - Refactored out all the general SASL code into sasl.c leaving only the cyrus specific code in cyrus_sasl.c - Created new default implementation in none_sasl.c
Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/df8e9ac5 Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/df8e9ac5 Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/df8e9ac5 Branch: refs/heads/rajith-codec Commit: df8e9ac5a0d811cc72d6095a9d337a91ade333b9 Parents: 136e33a Author: Andrew Stitcher <[email protected]> Authored: Tue Apr 28 00:28:55 2015 -0400 Committer: Andrew Stitcher <[email protected]> Committed: Wed May 6 16:01:11 2015 -0400 ---------------------------------------------------------------------- proton-c/src/engine/engine-internal.h | 3 - proton-c/src/sasl/cyrus_sasl.c | 864 +++++++---------------------- proton-c/src/sasl/none_sasl.c | 433 +++------------ proton-c/src/sasl/sasl-internal.h | 56 +- proton-c/src/sasl/sasl.c | 475 +++++++++++++++- proton-c/src/ssl/ssl-internal.h | 2 + 6 files changed, 798 insertions(+), 1035 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/df8e9ac5/proton-c/src/engine/engine-internal.h ---------------------------------------------------------------------- diff --git a/proton-c/src/engine/engine-internal.h b/proton-c/src/engine/engine-internal.h index 035d5be..748efbb 100644 --- a/proton-c/src/engine/engine-internal.h +++ b/proton-c/src/engine/engine-internal.h @@ -97,9 +97,6 @@ typedef struct { bool disp; } pn_session_state_t; -#include <proton/sasl.h> -#include <proton/ssl.h> - typedef struct pn_io_layer_t { ssize_t (*process_input)(struct pn_transport_t *transport, unsigned int layer, const char *, size_t); ssize_t (*process_output)(struct pn_transport_t *transport, unsigned int layer, char *, size_t); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/df8e9ac5/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 58d6c9b..a12f8c0 100644 --- a/proton-c/src/sasl/cyrus_sasl.c +++ b/proton-c/src/sasl/cyrus_sasl.c @@ -21,10 +21,7 @@ #include "sasl-internal.h" -#include "protocol.h" -#include "dispatch_actions.h" #include "engine/engine-internal.h" -#include "proton/codec.h" #include <sasl/sasl.h> @@ -37,145 +34,8 @@ # define sasl_server_done() #endif -enum pni_sasl_state { - SASL_NONE, - SASL_POSTED_INIT, - SASL_POSTED_MECHANISMS, - SASL_POSTED_RESPONSE, - SASL_POSTED_CHALLENGE, - SASL_PRETEND_OUTCOME, - SASL_RECVED_OUTCOME, - SASL_POSTED_OUTCOME -}; - -struct pni_sasl_t { - // Client selected mechanism - char *selected_mechanism; - char *included_mechanisms; - const char *username; - char *password; - const char *config_name; - char *config_dir; - const char *remote_fqdn; - sasl_conn_t *cyrus_conn; - pn_sasl_outcome_t outcome; - pn_bytes_t cyrus_out; - enum pni_sasl_state desired_state; - enum pni_sasl_state last_state; - bool client; - bool halt; -}; - -static bool pni_sasl_is_server_state(enum pni_sasl_state state) -{ - return state==SASL_NONE - || state==SASL_POSTED_MECHANISMS - || state==SASL_POSTED_CHALLENGE - || state==SASL_POSTED_OUTCOME; -} - -static bool pni_sasl_is_client_state(enum pni_sasl_state state) -{ - return state==SASL_NONE - || state==SASL_POSTED_INIT - || state==SASL_POSTED_RESPONSE - || state==SASL_PRETEND_OUTCOME - || state==SASL_RECVED_OUTCOME; -} - -static bool pni_sasl_is_final_input_state(pni_sasl_t *sasl) -{ - enum pni_sasl_state last_state = sasl->last_state; - enum pni_sasl_state desired_state = sasl->desired_state; - return last_state==SASL_RECVED_OUTCOME - || desired_state==SASL_POSTED_OUTCOME; -} - -static bool pni_sasl_is_final_output_state(pni_sasl_t *sasl) -{ - enum pni_sasl_state last_state = sasl->last_state; - return last_state==SASL_PRETEND_OUTCOME - || last_state==SASL_RECVED_OUTCOME - || last_state==SASL_POSTED_OUTCOME; -} - static const char *amqp_service = "amqp"; -static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl) -{ - // The external pn_sasl_t is really a pointer to the internal pni_transport_t - return ((pn_transport_t *)sasl); -} - -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 - return sasl ? ((pn_transport_t *)sasl)->sasl : NULL; -} - -static void pni_emit(pn_transport_t *transport) -{ - if (transport->connection && transport->connection->collector) { - pn_collector_t *collector = transport->connection->collector; - pn_collector_put(collector, PN_OBJECT, transport, PN_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_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 (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. -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_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; - } - } -} - static bool pni_check_sasl_result(sasl_conn_t *conn, int r, pn_transport_t *logger) { if (r!=SASL_OK) { @@ -208,604 +68,290 @@ static void pni_cyrus_interact(pni_sasl_t *sasl, sasl_interact_t *interact) } } -static void pni_sasl_set_desired_state(pn_transport_t *transport, enum pni_sasl_state desired_state) +int pni_sasl_impl_list_mechs(pn_transport_t *transport, char **mechlist) { pni_sasl_t *sasl = transport->sasl; - if (sasl->last_state > desired_state) { - pn_transport_logf(transport, "Trying to send SASL frame (%d), but illegal: already in later state (%d)", desired_state, sasl->last_state); - } else if (sasl->client && !pni_sasl_is_client_state(desired_state)) { - pn_transport_logf(transport, "Trying to send server SASL frame (%d) on a client", desired_state); - } else if (!sasl->client && !pni_sasl_is_server_state(desired_state)) { - pn_transport_logf(transport, "Trying to send client SASL frame (%d) on a server", desired_state); - } else { - // If we need to repeat CHALLENGE or RESPONSE frames adjust current state to seem - // like they haven't been sent yet - if (sasl->last_state==desired_state && desired_state==SASL_POSTED_RESPONSE) { - sasl->last_state = SASL_POSTED_INIT; - } - if (sasl->last_state==desired_state && desired_state==SASL_POSTED_CHALLENGE) { - sasl->last_state = SASL_POSTED_MECHANISMS; - } - sasl->desired_state = desired_state; - } -} - -// Post SASL frame -static void pni_post_sasl_frame(pn_transport_t *transport) -{ - pni_sasl_t *sasl = transport->sasl; - pn_bytes_t out = sasl->cyrus_out; - enum pni_sasl_state desired_state = sasl->desired_state; - while (sasl->desired_state > sasl->last_state) { - switch (desired_state) { - case SASL_POSTED_INIT: - pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[sz]", SASL_INIT, sasl->selected_mechanism, - out.size, out.start); - pni_emit(transport); - break; - case SASL_PRETEND_OUTCOME: - if (sasl->last_state < SASL_POSTED_INIT) { - desired_state = SASL_POSTED_INIT; - continue; + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; + 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 = strdup(result); } - break; - case SASL_POSTED_MECHANISMS: { - // TODO: Hardcoded limit of 16 mechanisms - char *mechs[16]; - int count = 0; - - char *mechlist = NULL; - if (sasl->cyrus_conn) { - const char *result = NULL; - - int r = sasl_listmech(sasl->cyrus_conn, NULL, "", " ", "", &result, NULL, NULL); - if (pni_check_sasl_result(sasl->cyrus_conn, r, transport)) { - if (result && *result) { - mechlist = strdup(result); - pni_split_mechs(mechlist, sasl->included_mechanisms, mechs, &count); - } - } - } - pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[@T[*s]]", SASL_MECHANISMS, PN_SYMBOL, count, mechs); - free(mechlist); - pni_emit(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); - break; - case SASL_POSTED_CHALLENGE: - if (sasl->last_state < SASL_POSTED_MECHANISMS) { - desired_state = SASL_POSTED_MECHANISMS; - continue; - } - 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) { - desired_state = SASL_POSTED_MECHANISMS; - continue; - } - pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[B]", SASL_OUTCOME, sasl->outcome); - pni_emit(transport); - break; - case SASL_NONE: - case SASL_RECVED_OUTCOME: - return; - } - sasl->last_state = desired_state; - desired_state = sasl->desired_state; } + return count; } // Set up callbacks to use interact static const sasl_callback_t pni_user_password_callbacks[] = { - {SASL_CB_USER, NULL, NULL}, - {SASL_CB_AUTHNAME, NULL, NULL}, - {SASL_CB_PASS, NULL, NULL}, - {SASL_CB_LIST_END, NULL, NULL}, + {SASL_CB_USER, NULL, NULL}, + {SASL_CB_AUTHNAME, NULL, NULL}, + {SASL_CB_PASS, NULL, NULL}, + {SASL_CB_LIST_END, NULL, NULL}, }; static const sasl_callback_t pni_user_callbacks[] = { - {SASL_CB_USER, NULL, NULL}, - {SASL_CB_AUTHNAME, NULL, NULL}, - {SASL_CB_LIST_END, NULL, NULL}, + {SASL_CB_USER, NULL, NULL}, + {SASL_CB_AUTHNAME, NULL, NULL}, + {SASL_CB_LIST_END, NULL, NULL}, }; +bool pni_init_client(pn_transport_t* transport) { + int result; + pni_sasl_t *sasl = transport->sasl; + + if (sasl->config_dir) { + result = sasl_set_path(SASL_PATH_TYPE_CONFIG, sasl->config_dir); + if (result!=SASL_OK) return false; + } + + result = sasl_client_init(NULL); + if (result!=SASL_OK) return false; + + const sasl_callback_t *callbacks = sasl->username ? sasl->password ? pni_user_password_callbacks : pni_user_callbacks : NULL; + result = sasl_client_new(amqp_service, + sasl->remote_fqdn, + NULL, NULL, + callbacks, 0, + (sasl_conn_t**)&sasl->impl_context); + if (result!=SASL_OK) return false; + + return true; +} + static int pni_wrap_client_start(pni_sasl_t *sasl, const char *mechs, const char **mechusing) { - int result; + int result; + sasl_interact_t *client_interact=NULL; + const char *out; + unsigned outlen; - if (sasl->config_dir) { - result = sasl_set_path(SASL_PATH_TYPE_CONFIG, sasl->config_dir); - if (result!=SASL_OK) return result; - } + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; + do { - result = sasl_client_init(NULL); - if (result!=SASL_OK) return result; - - const sasl_callback_t *callbacks = sasl->username ? sasl->password ? pni_user_password_callbacks : pni_user_callbacks : NULL; - result = sasl_client_new(amqp_service, - sasl->remote_fqdn, - NULL, NULL, - callbacks, 0, - &sasl->cyrus_conn); - if (result!=SASL_OK) return result; - - sasl_interact_t *client_interact=NULL; - const char *out; - unsigned outlen; - - do { - - result = sasl_client_start(sasl->cyrus_conn, - mechs, - &client_interact, - &out, &outlen, - mechusing); - if (result==SASL_INTERACT) { - pni_cyrus_interact(sasl, client_interact); - } - } while (result==SASL_INTERACT); + result = sasl_client_start(cyrus_conn, + mechs, + &client_interact, + &out, &outlen, + mechusing); + if (result==SASL_INTERACT) { + pni_cyrus_interact(sasl, client_interact); + } + } while (result==SASL_INTERACT); - sasl->cyrus_out.start = out; - sasl->cyrus_out.size = outlen; - return result; + sasl->cyrus_out.start = out; + sasl->cyrus_out.size = outlen; + return result; } -static void pni_process_mechanisms(pn_transport_t *transport, const char *mechs, bool short_circuit) +bool pni_process_mechanisms(pn_transport_t *transport, const char *mechs) { - pni_sasl_t *sasl = transport->sasl; - const char *mech_selected; - int result = pni_wrap_client_start(sasl, mechs, &mech_selected); - switch (result) { - case SASL_OK: - case SASL_CONTINUE: - sasl->selected_mechanism = strdup(mech_selected); - pni_sasl_set_desired_state(transport, short_circuit ? SASL_PRETEND_OUTCOME : SASL_POSTED_INIT); - break; - case SASL_NOMECH: - default: - pni_check_sasl_result(sasl->cyrus_conn, result, transport); - sasl->last_state = SASL_RECVED_OUTCOME; - sasl->halt = true; - pn_transport_close_tail(transport); - break; - } + pni_sasl_t *sasl = transport->sasl; + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; + const char *mech_selected; + int result = pni_wrap_client_start(sasl, mechs, &mech_selected); + switch (result) { + case SASL_OK: + case SASL_CONTINUE: + sasl->selected_mechanism = strdup(mech_selected); + return true; + case SASL_NOMECH: + default: + pni_check_sasl_result(cyrus_conn, result, transport); + return false; + } } static int pni_wrap_client_step(pni_sasl_t *sasl, const pn_bytes_t *in) { - sasl_interact_t *client_interact=NULL; - const char *out; - unsigned outlen; - - int result; - do { + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; + sasl_interact_t *client_interact=NULL; + const char *out; + unsigned outlen; - result = sasl_client_step(sasl->cyrus_conn, - in->start, in->size, - &client_interact, - &out, &outlen); - if (result==SASL_INTERACT) { - pni_cyrus_interact(sasl, client_interact); - } - } while (result==SASL_INTERACT); + int result; + do { + + result = sasl_client_step(cyrus_conn, + in->start, in->size, + &client_interact, + &out, &outlen); + if (result==SASL_INTERACT) { + pni_cyrus_interact(sasl, client_interact); + } + } while (result==SASL_INTERACT); - sasl->cyrus_out.start = out; - sasl->cyrus_out.size = outlen; - return result; + sasl->cyrus_out.start = out; + sasl->cyrus_out.size = outlen; + return result; } -static void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv) +void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv) { - pni_sasl_t *sasl = transport->sasl; - int result = pni_wrap_client_step(sasl, 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); - break; - default: - pni_check_sasl_result(sasl->cyrus_conn, result, transport); - - // Failed somehow - sasl->halt = true; - pn_transport_close_tail(transport); - break; - } + 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); + 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); + break; + default: + pni_check_sasl_result(cyrus_conn, result, transport); + + // Failed somehow + sasl->halt = true; + pn_transport_close_tail(transport); + break; + } } static int pni_wrap_server_new(pn_transport_t *transport) { - pni_sasl_t *sasl = transport->sasl; - int result; + pni_sasl_t *sasl = transport->sasl; + int result; - if (sasl->config_dir) { - result = sasl_set_path(SASL_PATH_TYPE_CONFIG, sasl->config_dir); - if (result!=SASL_OK) return result; - } + if (sasl->config_dir) { + result = sasl_set_path(SASL_PATH_TYPE_CONFIG, sasl->config_dir); + if (result!=SASL_OK) return result; + } - result = sasl_server_init(NULL, sasl->config_name); - if (result!=SASL_OK) return result; + result = sasl_server_init(NULL, sasl->config_name); + if (result!=SASL_OK) return result; - result = sasl_server_new(amqp_service, NULL, NULL, NULL, NULL, NULL, 0, &sasl->cyrus_conn); - if (result!=SASL_OK) return result; + result = sasl_server_new(amqp_service, NULL, NULL, NULL, NULL, NULL, 0, (sasl_conn_t**)&sasl->impl_context); + if (result!=SASL_OK) return result; - sasl_security_properties_t secprops = {0}; - secprops.security_flags = + sasl_security_properties_t secprops = {0}; + secprops.security_flags = SASL_SEC_NOPLAINTEXT | ( transport->auth_required ? SASL_SEC_NOANONYMOUS : 0 ) ; + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; - result = sasl_setprop(sasl->cyrus_conn, SASL_SEC_PROPS, &secprops); - if (result!=SASL_OK) return result; + result = sasl_setprop(cyrus_conn, SASL_SEC_PROPS, &secprops); + if (result!=SASL_OK) return result; - // EXTERNAL not implemented yet -#if 0 - sasl_ssf_t ssf = 128; - result = sasl_setprop(sasl->cyrus_conn, SASL_SSF_EXTERNAL, &ssf); - if (result!=SASL_OK) return result; + // EXTERNAL not implemented yet + #if 0 + sasl_ssf_t ssf = 128; + result = sasl_setprop(sasl->cyrus_conn, SASL_SSF_EXTERNAL, &ssf); + if (result!=SASL_OK) return result; - const char *extid = "user"; - result = sasl_setprop(sasl->cyrus_conn, SASL_AUTH_EXTERNAL, extid); - if (result!=SASL_OK) return result; -#endif + const char *extid = "user"; + result = sasl_setprop(sasl->cyrus_conn, SASL_AUTH_EXTERNAL, extid); + if (result!=SASL_OK) return result; + #endif - return result; + return result; } static int pni_wrap_server_start(pni_sasl_t *sasl, const char *mech_selected, const pn_bytes_t *in) { - int result; - const char *out; - unsigned outlen; - result = sasl_server_start(sasl->cyrus_conn, - mech_selected, - in->start, in->size, - &out, &outlen); - - sasl->cyrus_out.start = out; - sasl->cyrus_out.size = outlen; - return result; -} - -static void pni_process_server_result(pn_transport_t *transport, int result) -{ - pni_sasl_t *sasl = transport->sasl; - switch (result) { - case SASL_OK: - // Authenticated - sasl->outcome = PN_SASL_OK; - transport->authenticated = true; - // Get username from SASL - const void* value; - sasl_getprop(sasl->cyrus_conn, SASL_USERNAME, &value); - sasl->username = (const char*) value; - pn_transport_logf(transport, "Authenticated user: %s with mechanism %s", sasl->username, sasl->selected_mechanism); - pni_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); - break; - default: - pni_check_sasl_result(sasl->cyrus_conn, result, transport); - - // Failed to authenticate - sasl->outcome = PN_SASL_AUTH; - pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); - break; - } - pni_emit(transport); -} - -static void pni_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); - 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_seterror(sasl->cyrus_conn, 0, "Client mechanism not in mechanism inclusion list."); - result = SASL_FAIL; - } - } - pni_process_server_result(transport, result); -} - -static int pni_wrap_server_step(pni_sasl_t *sasl, const pn_bytes_t *in) -{ int result; const char *out; unsigned outlen; - result = sasl_server_step(sasl->cyrus_conn, - in->start, in->size, - &out, &outlen); + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; + result = sasl_server_start(cyrus_conn, + mech_selected, + in->start, in->size, + &out, &outlen); sasl->cyrus_out.start = out; sasl->cyrus_out.size = outlen; return result; } -static void pni_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); - pni_process_server_result(transport, result); -} - -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->client = !transport->server; - sasl->selected_mechanism = NULL; - sasl->included_mechanisms = NULL; - sasl->username = NULL; - sasl->password = NULL; - sasl->config_name = sasl->client ? "proton-client" : "proton-server"; - sasl->config_dir = sasl_config_path ? strdup(sasl_config_path) : NULL; - sasl->remote_fqdn = NULL; - sasl->outcome = PN_SASL_NONE; - sasl->cyrus_conn = NULL; - sasl->cyrus_out.size = 0; - sasl->cyrus_out.start = NULL; - sasl->desired_state = SASL_NONE; - sasl->last_state = SASL_NONE; - sasl->halt = false; - - transport->sasl = sasl; - } - - // The actual external pn_sasl_t pointer is a pointer to its enclosing pn_transport_t - return (pn_sasl_t *)transport; -} - -// This is a hack to tell us that -// no actual negotiation is going to happen and we can go -// straight to the AMQP layer; it can only work on the client side -// As the server doesn't know if SASL is even active until it sees -// the SASL header from the client first. -static void pni_sasl_force_anonymous(pn_transport_t *transport) -{ - pni_sasl_t *sasl = transport->sasl; - if (sasl->client) { - // Pretend we got sasl mechanisms frame with just ANONYMOUS - pni_process_mechanisms(transport, "ANONYMOUS", true); - } - pni_emit(transport); -} - -void pni_sasl_set_remote_hostname(pn_transport_t * transport, const char * fqdn) -{ - pni_sasl_t *sasl = transport->sasl; - sasl->remote_fqdn = fqdn; -} - -void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, const char *password) -{ - pni_sasl_t *sasl = transport->sasl; - sasl->username = user; - free(sasl->password); - sasl->password = password ? strdup(password) : NULL; -} - -const char *pn_sasl_get_user(pn_sasl_t *sasl0) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - return sasl->username; -} - -const char *pn_sasl_get_mech(pn_sasl_t *sasl0) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - return sasl->selected_mechanism; -} - -void pn_sasl_allowed_mechs(pn_sasl_t *sasl0, const char *mechs) +static void pni_process_server_result(pn_transport_t *transport, int result) { - pni_sasl_t *sasl = get_sasl_internal(sasl0); - free(sasl->included_mechanisms); - sasl->included_mechanisms = mechs ? strdup(mechs) : NULL; - if (strcmp(mechs, "ANONYMOUS")==0 ) { - pn_transport_t *transport = get_transport_internal(sasl0); - pni_sasl_force_anonymous(transport); + pni_sasl_t *sasl = transport->sasl; + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; + switch (result) { + 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; + pn_transport_logf(transport, "Authenticated user: %s with mechanism %s", sasl->username, sasl->selected_mechanism); + pni_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); + 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); + break; } } -void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - sasl->config_name = 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 = strdup(dir); -} - -void pn_sasl_done(pn_sasl_t *sasl0, pn_sasl_outcome_t outcome) +bool pni_init_server(pn_transport_t* transport) { - pni_sasl_t *sasl = get_sasl_internal(sasl0); - if (sasl) { - sasl->outcome = outcome; - } + int r = pni_wrap_server_new(transport); + sasl_conn_t *cyrus_conn = (sasl_conn_t*)transport->sasl->impl_context; + return pni_check_sasl_result(cyrus_conn, r, transport); } -pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl0) +void pni_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv) { - pni_sasl_t *sasl = get_sasl_internal(sasl0); - return sasl ? sasl->outcome : PN_SASL_NONE; -} - -void pn_sasl_free(pn_transport_t *transport) -{ - if (transport) { pni_sasl_t *sasl = transport->sasl; - if (sasl) { - free(sasl->selected_mechanism); - free(sasl->included_mechanisms); - free(sasl->password); - free(sasl->config_dir); - - // CYRUS_SASL - if (sasl->cyrus_conn) { - sasl_dispose(&sasl->cyrus_conn); - if (sasl->client) { - sasl_client_done(); - } else { - sasl_server_done(); - } - } - - free(sasl); - } - } -} - -static void pni_sasl_server_init(pn_transport_t *transport) -{ - int r = pni_wrap_server_new(transport); - - if (!pni_check_sasl_result(transport->sasl->cyrus_conn, r, transport)) { - return; - } - // Setup to send SASL mechanisms frame - pni_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS); -} - -static void pn_sasl_process(pn_transport_t *transport) -{ - pni_sasl_t *sasl = transport->sasl; - if (!sasl->client) { - if (sasl->desired_state<SASL_POSTED_MECHANISMS) { - pni_sasl_server_init(transport); + int result = pni_wrap_server_start(sasl, 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; + sasl_seterror(cyrus_conn, 0, "Client mechanism not in mechanism inclusion list."); + result = SASL_FAIL; + } } - } + pni_process_server_result(transport, result); } -ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available) -{ - pn_sasl_process(transport); - - pni_sasl_t *sasl = transport->sasl; - ssize_t n = pn_dispatcher_input(transport, bytes, available, false, &sasl->halt); - - if (n==0 && pni_sasl_is_final_input_state(sasl)) { - return PN_EOS; - } - return n; -} - -ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size) +static int pni_wrap_server_step(pni_sasl_t *sasl, const pn_bytes_t *in) { - pn_sasl_process(transport); - - pni_post_sasl_frame(transport); + int result; + const char *out; + unsigned outlen; + sasl_conn_t *cyrus_conn = (sasl_conn_t*)sasl->impl_context; + result = sasl_server_step(cyrus_conn, + in->start, in->size, + &out, &outlen); - pni_sasl_t *sasl = transport->sasl; - if (transport->available == 0 && pni_sasl_is_final_output_state(sasl)) { - if (sasl->outcome != PN_SASL_OK && pni_sasl_is_final_input_state(sasl)) { - pn_transport_close_tail(transport); - } - return PN_EOS; - } else { - return pn_dispatcher_output(transport, bytes, size); - } + sasl->cyrus_out.start = out; + sasl->cyrus_out.size = outlen; + return result; } -// Received Server side -int pn_do_init(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +void pni_process_response(pn_transport_t *transport, const pn_bytes_t *recv) { - pni_sasl_t *sasl = transport->sasl; - pn_bytes_t mech; - pn_bytes_t recv; - int err = pn_data_scan(args, "D.[sz]", &mech, &recv); - if (err) return err; - sasl->selected_mechanism = pn_strndup(mech.start, mech.size); - - pni_process_init(transport, sasl->selected_mechanism, &recv); - - return 0; + pni_sasl_t *sasl = transport->sasl; + int result = pni_wrap_server_step(sasl, recv); + pni_process_server_result(transport, result); } -// Received client side -int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +void pni_sasl_impl_free(pn_transport_t *transport) { - // If we already pretended we got the ANONYMOUS mech then ignore - if (transport->sasl->last_state==SASL_PRETEND_OUTCOME) return 0; - - pn_string_t *mechs = pn_string(""); - - // This scanning relies on pn_data_scan leaving the pn_data_t cursors - // where they are after finishing the scan - int err = pn_data_scan(args, "D.[@["); - if (err) return err; - - // 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)) { - pn_string_addf(mechs, "%*s ", (int)s.size, s.start); + sasl_dispose((sasl_conn_t**)&transport->sasl->impl_context); + if (transport->sasl->client) { + sasl_client_done(); + } else { + sasl_server_done(); } - } - pn_string_buffer(mechs)[pn_string_size(mechs)-1] = 0; - - pni_process_mechanisms(transport, pn_string_get(mechs), false); - - pn_free(mechs); - return 0; -} - -// Received client side -int pn_do_challenge(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) -{ - pn_bytes_t recv; - int err = pn_data_scan(args, "D.[z]", &recv); - if (err) return err; - - pni_process_challenge(transport, &recv); - - return 0; -} - -// Received server side -int pn_do_response(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) -{ - pn_bytes_t recv; - int err = pn_data_scan(args, "D.[z]", &recv); - if (err) return err; - - pni_process_response(transport, &recv); - - return 0; -} - -// Received client side -int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) -{ - pni_sasl_t *sasl = transport->sasl; - uint8_t outcome; - int err = pn_data_scan(args, "D.[B]", &outcome); - if (err) return err; - sasl->outcome = (pn_sasl_outcome_t) outcome; - sasl->last_state = SASL_RECVED_OUTCOME; - sasl->halt = true; - pni_emit(transport); - return 0; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/df8e9ac5/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 index ff962ac..6900654 100644 --- a/proton-c/src/sasl/none_sasl.c +++ b/proton-c/src/sasl/none_sasl.c @@ -19,408 +19,125 @@ * */ -#include <assert.h> -#include <proton/codec.h> +#include "sasl-internal.h" -#include "buffer.h" -#include "protocol.h" -#include "dispatch_actions.h" -#include "util.h" #include "engine/engine-internal.h" -struct pni_sasl_t { - char *selected_mechanism; - char *included_mechanisms; - const char *username; - char *password; - const char *remote_fqdn; - pn_buffer_t *send_data; - pn_buffer_t *recv_data; - pn_sasl_outcome_t outcome; - bool client; - bool sent_init; - bool rcvd_init; - bool sent_done; - bool rcvd_done; - bool halt; -}; - -static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl) -{ - // The external pn_sasl_t is really a pointer to the internal pni_transport_t - return ((pn_transport_t *)sasl); -} - -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 - return sasl ? ((pn_transport_t *)sasl)->sasl : NULL; -} - -static void pni_emit(pn_sasl_t *sasl) { - pn_transport_t *transport = get_transport_internal(sasl); - if (transport->connection && transport->connection->collector) { - pn_collector_t *collector = transport->connection->collector; - pn_collector_put(collector, PN_OBJECT, transport, PN_TRANSPORT); - } -} - -pn_sasl_t *pn_sasl(pn_transport_t *transport) -{ - if (!transport->sasl) { - pni_sasl_t *sasl = (pni_sasl_t *) malloc(sizeof(pni_sasl_t)); - - sasl->client = !transport->server; - sasl->included_mechanisms = NULL; - sasl->selected_mechanism = NULL; - sasl->send_data = pn_buffer(16); - sasl->recv_data = pn_buffer(16); - sasl->outcome = PN_SASL_NONE; - sasl->sent_init = false; - sasl->rcvd_init = false; - sasl->sent_done = false; - sasl->rcvd_done = false; - sasl->halt = false; - - transport->sasl = sasl; - } - - // The actual external pn_sasl_t pointer is a pointer to its enclosing pn_transport_t - return (pn_sasl_t *)transport; -} - -void pn_sasl_allowed_mechs(pn_sasl_t *sasl0, const char *mechs) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - if (!sasl) return; - free(sasl->included_mechanisms); - sasl->included_mechanisms = mechs ? pn_strdup(mechs) : NULL; - if (strcmp(mechs, "ANONYMOUS")==0 ) { - // If we do this on the client it is a hack to tell us that - // no actual negatiation is going to happen and we can go - // straight to the AMQP layer - if (sasl->client) { - sasl->rcvd_done = true; - sasl->sent_done = true; - } - } - pni_emit(sasl0); -} - -ssize_t pn_sasl_send(pn_sasl_t *sasl0, const char *bytes, size_t size) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - if (sasl) { - if (pn_buffer_size(sasl->send_data)) { - // XXX: need better error - return PN_STATE_ERR; - } - int err = pn_buffer_append(sasl->send_data, bytes, size); - if (err) return err; - pni_emit(sasl0); - return size; - } else { - return PN_ARG_ERR; - } -} - -size_t pn_sasl_pending(pn_sasl_t *sasl0) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - if (sasl && pn_buffer_size(sasl->recv_data)) { - return pn_buffer_size(sasl->recv_data); - } else { - return 0; - } -} - -ssize_t pn_sasl_recv(pn_sasl_t *sasl0, char *bytes, size_t size) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - if (!sasl) return PN_ARG_ERR; - - size_t bsize = pn_buffer_size(sasl->recv_data); - if (bsize) { - if (bsize > size) return PN_OVERFLOW; - pn_buffer_get(sasl->recv_data, 0, bsize, bytes); - pn_buffer_clear(sasl->recv_data); - return bsize; - } else { - return PN_EOS; - } -} - -void pn_sasl_client(pn_sasl_t *sasl) -{ -} - -void pn_sasl_server(pn_sasl_t *sasl0) -{ - pni_sasl_t *sasl = get_sasl_internal(sasl0); - if (sasl) { - sasl->client = false; - } -} - -void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, const char *password) -{ - pni_sasl_t *sasl = transport->sasl; - sasl->username = user; - sasl->password = password ? pn_strdup(password) : NULL; -} - -const char *pn_sasl_get_user(pn_sasl_t *sasl0) -{ - return "anonymous"; -} - -const char *pn_sasl_get_mech(pn_sasl_t *sasl0) -{ - return "ANONYMOUS"; -} - -void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name) -{ -} - -void pn_sasl_config_path(pn_sasl_t *sasl0, const char *path) -{ -} - -void pni_sasl_set_remote_hostname(pn_transport_t * transport, const char * fqdn) -{ - pni_sasl_t *sasl = transport->sasl; - sasl->remote_fqdn = fqdn; -} - -void pn_sasl_plain(pn_sasl_t *sasl0, const char *username, const char *password) +#if 0 +// TODO: skeleton to produce PLAIN init frame +void pn_sasl_plain(pni_sasl_t *sasl, const char *username, const char *password) { - pni_sasl_t *sasl = get_sasl_internal(sasl0); if (!sasl) return; const char *user = username ? username : ""; const char *pass = password ? password : ""; - size_t usize = strlen(user); - size_t psize = strlen(pass); - size_t size = usize + psize + 2; - char *iresp = (char *) malloc(size); - iresp[0] = 0; - memmove(iresp + 1, user, usize); - iresp[usize + 1] = 0; - memmove(iresp + usize + 2, pass, psize); - - pn_sasl_allowed_mechs(sasl0, "PLAIN"); - pn_sasl_send(sasl0, iresp, size); - free(iresp); } +#endif +static const char ANONYMOUS[] = "ANONYMOUS"; +static const char PLAIN[] = "PLAIN"; -void pn_sasl_done(pn_sasl_t *sasl0, pn_sasl_outcome_t outcome) +bool pni_init_server(pn_transport_t* transport) { - pni_sasl_t *sasl = get_sasl_internal(sasl0); - if (sasl) { - sasl->outcome = outcome; - pni_emit(sasl0); - } + return true; } -pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl0) +bool pni_init_client(pn_transport_t* transport) { - pni_sasl_t *sasl = get_sasl_internal(sasl0); - return sasl ? sasl->outcome : PN_SASL_NONE; + return true; } -void pn_sasl_free(pn_transport_t *transport) +void pni_sasl_impl_free(pn_transport_t *transport) { - if (transport) { - pni_sasl_t *sasl = transport->sasl; - if (sasl) { - free(sasl->included_mechanisms); - free(sasl->selected_mechanism); - pn_buffer_free(sasl->send_data); - pn_buffer_free(sasl->recv_data); - free(sasl); - } - } + free(transport->sasl->impl_context); } -void pn_client_init(pn_transport_t *transport) +// Client handles ANONYMOUS or PLAIN mechanisms if offered +bool pni_process_mechanisms(pn_transport_t *transport, const char *mechs) { - pni_sasl_t *sasl = transport->sasl; - pn_buffer_memory_t bytes = pn_buffer_memory(sasl->send_data); - pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[sz]", SASL_INIT, sasl->included_mechanisms, - bytes.size, bytes.start); - pn_buffer_clear(sasl->send_data); - pni_emit((pn_sasl_t *) transport); -} + // Check whether offered ANONYMOUS or PLAIN + // Look for "PLAIN" in mechs + const char *found = strstr(mechs, PLAIN); + // Make sure that string is separated and terminated, allowed + // and we have a username and password + if (found && (found==mechs || found[-1]==' ') && (found[5]==0 || found[5]==' ') && + pni_included_mech(transport->sasl->included_mechanisms, pn_bytes(5, found)) && + transport->sasl->username && transport->sasl->password) { + transport->sasl->selected_mechanism = 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; -void pni_sasl_server_init(pn_transport_t *transport) -{ - pni_sasl_t *sasl = transport->sasl; - // XXX - char *mechs[16]; - int count = 0; + transport->sasl->impl_context = iresp; - if (sasl->included_mechanisms) { - char *start = sasl->included_mechanisms; - char *end = start; + iresp[0] = 0; + memmove(iresp + 1, transport->sasl->username, usize); + iresp[usize + 1] = 0; + memmove(iresp + usize + 2, transport->sasl->password, psize); + transport->sasl->cyrus_out.start = iresp; + transport->sasl->cyrus_out.size = size; - while (*end) { - if (*end == ' ') { - if (start != end) { - *end = '\0'; - mechs[count++] = start; - } - end++; - start = end; - } else { - end++; - } - } + // Zero out password and dealloc + free(memset(transport->sasl->password, 0, psize)); + transport->sasl->password = NULL; - if (start != end) { - mechs[count++] = start; - } + return true; } - pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[@T[*s]]", SASL_MECHANISMS, PN_SYMBOL, count, mechs); - pni_emit((pn_sasl_t *) transport); -} + // 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 = strdup(ANONYMOUS); + if (transport->sasl->username) { + size_t size = strlen(transport->sasl->username); + char *iresp = (char *) malloc(size); + if (!iresp) return false; -void pn_server_done(pn_sasl_t *sasl0) -{ - pn_transport_t *transport = get_transport_internal(sasl0); - pni_sasl_t *sasl = transport->sasl; - pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[B]", SASL_OUTCOME, sasl->outcome); - pni_emit(sasl0); -} + transport->sasl->impl_context = iresp; -void pn_sasl_process(pn_transport_t *transport) -{ - pni_sasl_t *sasl = transport->sasl; - if (!sasl->sent_init) { - if (sasl->client) { - pn_client_init(transport); + memmove(iresp, transport->sasl->username, size); + transport->sasl->cyrus_out.start = iresp; + transport->sasl->cyrus_out.size = size; } else { - pni_sasl_server_init(transport); + static const char anon[] = "anonymous"; + transport->sasl->cyrus_out.start = anon; + transport->sasl->cyrus_out.size = sizeof anon-1; } - sasl->sent_init = true; - } - - if (pn_buffer_size(sasl->send_data)) { - pn_buffer_memory_t bytes = pn_buffer_memory(sasl->send_data); - pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[z]", sasl->client ? SASL_RESPONSE : SASL_CHALLENGE, - bytes.size, bytes.start); - pn_buffer_clear(sasl->send_data); - pni_emit((pn_sasl_t *) transport); - } - - if (!sasl->client && sasl->outcome != PN_SASL_NONE && !sasl->sent_done) { - pn_server_done((pn_sasl_t *)transport); - sasl->sent_done = true; - } - - // XXX: need to finish this check when challenge/response is complete - // check for client is outome is received - // check for server is that there are no pending frames (either init - // or challenges) from client - if (!sasl->client && sasl->sent_done && sasl->rcvd_init) { - sasl->rcvd_done = true; - sasl->halt = true; + return true; } + return false; } -ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available) +// Server will offer only ANONYMOUS +int pni_sasl_impl_list_mechs(pn_transport_t *transport, char **mechlist) { - pni_sasl_t *sasl = transport->sasl; - ssize_t n = pn_dispatcher_input(transport, bytes, available, false, &sasl->halt); - if (n < 0) return n; - - pn_sasl_process(transport); - - if (sasl->rcvd_done) { - if (sasl->outcome == PN_SASL_OK) { - if (n) { - return n; - } else { - return PN_EOS; - } - } else { - // XXX: should probably do something better here - return PN_EOS; - } - } else { - return n; - } + *mechlist = strdup("ANONYMOUS"); + return 1; } -ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size) +void pni_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv) { - pn_sasl_process(transport); - - pni_sasl_t *sasl = transport->sasl; - if (transport->available == 0 && sasl->sent_done) { - if (sasl->outcome == PN_SASL_OK) { - return PN_EOS; - } else { - // XXX: should probably do something better here - return PN_EOS; - } + // 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; + pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); } else { - return pn_dispatcher_output(transport, bytes, size); + transport->sasl->outcome = PN_SASL_AUTH; + pni_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); } } -int pn_do_init(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) -{ - pni_sasl_t *sasl = transport->sasl; - pn_bytes_t mech; - pn_bytes_t recv; - int err = pn_data_scan(args, "D.[sz]", &mech, &recv); - if (err) return err; - sasl->selected_mechanism = pn_strndup(mech.start, mech.size); - pn_buffer_append(sasl->recv_data, recv.start, recv.size); - sasl->rcvd_init = true; - return 0; -} - -int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) -{ - pni_sasl_t *sasl = transport->sasl; - sasl->rcvd_init = true; - return 0; -} - -int pn_do_recv(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) -{ - pni_sasl_t *sasl = transport->sasl; - pn_bytes_t recv; - int err = pn_data_scan(args, "D.[z]", &recv); - if (err) return err; - pn_buffer_append(sasl->recv_data, recv.start, recv.size); - return 0; -} - -int pn_do_challenge(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) -{ - return pn_do_recv(transport, frame_type, channel, args, payload); -} - -int pn_do_response(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +/* The default implementation neither sends nor receives challenges or responses */ +void pni_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv) { - return pn_do_recv(transport, frame_type, channel, args, payload); } -int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +void pni_process_response(pn_transport_t *transport, const pn_bytes_t *recv) { - pni_sasl_t *sasl = transport->sasl; - uint8_t outcome; - int err = pn_data_scan(args, "D.[B]", &outcome); - if (err) return err; - sasl->outcome = (pn_sasl_outcome_t) outcome; - sasl->rcvd_done = true; - sasl->sent_done = true; - sasl->halt = true; - pni_emit((pn_sasl_t *) transport); - return 0; } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/df8e9ac5/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 965eff2..b2d30c8 100644 --- a/proton-c/src/sasl/sasl-internal.h +++ b/proton-c/src/sasl/sasl-internal.h @@ -23,18 +23,56 @@ #define PROTON_SASL_INTERNAL_H 1 #include "proton/types.h" +#include "proton/sasl.h" -/** Destructor for the given SASL layer. - * - * @param[in] sasl the SASL object to free. No longer valid on - * return. - */ +// SASL APIs used by transport code void pn_sasl_free(pn_transport_t *transport); - -ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available); -ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size); - void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, const char *password); void pni_sasl_set_remote_hostname(pn_transport_t *transport, const char* fqdn); +// 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); + +// 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_PRETEND_OUTCOME, + SASL_RECVED_OUTCOME, + SASL_POSTED_OUTCOME +}; + +struct pni_sasl_t { + void *impl_context; + // Client selected mechanism + char *selected_mechanism; + char *included_mechanisms; + const char *username; + char *password; + const char *config_name; + char *config_dir; + const char *remote_fqdn; + pn_sasl_outcome_t outcome; + pn_bytes_t cyrus_out; + enum pni_sasl_state desired_state; + enum pni_sasl_state last_state; + bool client; + bool halt; +}; + +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/df8e9ac5/proton-c/src/sasl/sasl.c ---------------------------------------------------------------------- diff --git a/proton-c/src/sasl/sasl.c b/proton-c/src/sasl/sasl.c index 0c58b1f..2b59460 100644 --- a/proton-c/src/sasl/sasl.c +++ b/proton-c/src/sasl/sasl.c @@ -21,7 +21,10 @@ #include "sasl-internal.h" +#include "dispatch_actions.h" #include "engine/engine-internal.h" +#include "protocol.h" +#include "platform.h" // For strncasecmp on Windows #include "transport/autodetect.h" #include <assert.h> @@ -32,12 +35,8 @@ static inline pn_transport_t *get_transport_internal(pn_sasl_t *sasl) return ((pn_transport_t *)sasl); } -void pn_sasl_allow_skip(pn_sasl_t *sasl0, bool allow) -{ - if (!sasl0) return; - pn_transport_t *transport = get_transport_internal(sasl0); - pn_transport_require_auth(transport, !allow); -} +static ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available); +static ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size); static ssize_t pn_input_read_sasl_header(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available); static ssize_t pn_input_read_sasl(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available); @@ -159,3 +158,467 @@ static ssize_t pn_output_write_sasl(pn_transport_t* transport, unsigned int laye return pni_passthru_layer.process_output(transport, layer, bytes, available ); } +static bool pni_sasl_is_server_state(enum pni_sasl_state state) +{ + return state==SASL_NONE + || state==SASL_POSTED_MECHANISMS + || state==SASL_POSTED_CHALLENGE + || state==SASL_POSTED_OUTCOME; +} + +static bool pni_sasl_is_client_state(enum pni_sasl_state state) +{ + return state==SASL_NONE + || state==SASL_POSTED_INIT + || state==SASL_POSTED_RESPONSE + || state==SASL_PRETEND_OUTCOME + || state==SASL_RECVED_OUTCOME; +} + +static bool pni_sasl_is_final_input_state(pni_sasl_t *sasl) +{ + enum pni_sasl_state last_state = sasl->last_state; + enum pni_sasl_state desired_state = sasl->desired_state; + return last_state==SASL_RECVED_OUTCOME + || desired_state==SASL_POSTED_OUTCOME; +} + +static bool pni_sasl_is_final_output_state(pni_sasl_t *sasl) +{ + enum pni_sasl_state last_state = sasl->last_state; + return last_state==SASL_PRETEND_OUTCOME + || last_state==SASL_RECVED_OUTCOME + || last_state==SASL_POSTED_OUTCOME; +} + +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 + return sasl ? ((pn_transport_t *)sasl)->sasl : NULL; +} + +static void pni_emit(pn_transport_t *transport) +{ + if (transport->connection && transport->connection->collector) { + pn_collector_t *collector = transport->connection->collector; + pn_collector_put(collector, PN_OBJECT, transport, PN_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. +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 (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; + } + } +} + +void pni_sasl_set_desired_state(pn_transport_t *transport, enum pni_sasl_state desired_state) +{ + pni_sasl_t *sasl = transport->sasl; + if (sasl->last_state > desired_state) { + pn_transport_logf(transport, "Trying to send SASL frame (%d), but illegal: already in later state (%d)", desired_state, sasl->last_state); + } else if (sasl->client && !pni_sasl_is_client_state(desired_state)) { + pn_transport_logf(transport, "Trying to send server SASL frame (%d) on a client", desired_state); + } else if (!sasl->client && !pni_sasl_is_server_state(desired_state)) { + pn_transport_logf(transport, "Trying to send client SASL frame (%d) on a server", desired_state); + } else { + // If we need to repeat CHALLENGE or RESPONSE frames adjust current state to seem + // like they haven't been sent yet + if (sasl->last_state==desired_state && desired_state==SASL_POSTED_RESPONSE) { + sasl->last_state = SASL_POSTED_INIT; + } + if (sasl->last_state==desired_state && desired_state==SASL_POSTED_CHALLENGE) { + sasl->last_state = SASL_POSTED_MECHANISMS; + } + sasl->desired_state = desired_state; + pni_emit(transport); + } +} + +// Post SASL frame +static void pni_post_sasl_frame(pn_transport_t *transport) +{ + pni_sasl_t *sasl = transport->sasl; + pn_bytes_t out = sasl->cyrus_out; + enum pni_sasl_state desired_state = sasl->desired_state; + while (sasl->desired_state > sasl->last_state) { + switch (desired_state) { + case SASL_POSTED_INIT: + pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[sz]", SASL_INIT, sasl->selected_mechanism, + out.size, out.start); + pni_emit(transport); + break; + case SASL_PRETEND_OUTCOME: + if (sasl->last_state < SASL_POSTED_INIT) { + desired_state = SASL_POSTED_INIT; + continue; + } + break; + case SASL_POSTED_MECHANISMS: { + // TODO: Hardcoded limit of 16 mechanisms + char *mechs[16]; + char *mechlist = NULL; + + int count = 0; + if (pni_sasl_impl_list_mechs(transport, &mechlist) > 0) { + pni_split_mechs(mechlist, sasl->included_mechanisms, mechs, &count); + } + + pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[@T[*s]]", SASL_MECHANISMS, PN_SYMBOL, count, mechs); + free(mechlist); + pni_emit(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); + break; + case SASL_POSTED_CHALLENGE: + if (sasl->last_state < SASL_POSTED_MECHANISMS) { + desired_state = SASL_POSTED_MECHANISMS; + continue; + } + 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) { + desired_state = SASL_POSTED_MECHANISMS; + continue; + } + pn_post_frame(transport, SASL_FRAME_TYPE, 0, "DL[B]", SASL_OUTCOME, sasl->outcome); + pni_emit(transport); + break; + case SASL_NONE: + case SASL_RECVED_OUTCOME: + return; + } + sasl->last_state = desired_state; + desired_state = sasl->desired_state; + } +} + +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; + sasl->client = !transport->server; + sasl->selected_mechanism = NULL; + sasl->included_mechanisms = NULL; + sasl->username = NULL; + sasl->password = NULL; + sasl->config_name = sasl->client ? "proton-client" : "proton-server"; + sasl->config_dir = sasl_config_path ? strdup(sasl_config_path) : NULL; + sasl->remote_fqdn = NULL; + sasl->outcome = PN_SASL_NONE; + sasl->impl_context = NULL; + sasl->cyrus_out.size = 0; + sasl->cyrus_out.start = NULL; + sasl->desired_state = SASL_NONE; + sasl->last_state = SASL_NONE; + sasl->halt = false; + + transport->sasl = sasl; + } + + // The actual external pn_sasl_t pointer is a pointer to its enclosing pn_transport_t + return (pn_sasl_t *)transport; +} + +// This is a hack to tell us that +// no actual negotiation is going to happen and we can go +// straight to the AMQP layer; it can only work on the client side +// As the server doesn't know if SASL is even active until it sees +// the SASL header from the client first. +static void pni_sasl_force_anonymous(pn_transport_t *transport) +{ + pni_sasl_t *sasl = transport->sasl; + if (sasl->client) { + // Pretend we got sasl mechanisms frame with just ANONYMOUS + if (pni_init_client(transport) && + pni_process_mechanisms(transport, "ANONYMOUS")) { + pni_sasl_set_desired_state(transport, SASL_PRETEND_OUTCOME); + } else { + sasl->last_state = SASL_RECVED_OUTCOME; + sasl->halt = true; + pn_transport_close_tail(transport); + } + } +} + +void pni_sasl_set_remote_hostname(pn_transport_t * transport, const char * fqdn) +{ + pni_sasl_t *sasl = transport->sasl; + sasl->remote_fqdn = fqdn; +} + +void pni_sasl_set_user_password(pn_transport_t *transport, const char *user, const char *password) +{ + pni_sasl_t *sasl = transport->sasl; + sasl->username = user; + free(sasl->password); + sasl->password = password ? strdup(password) : NULL; +} + +const char *pn_sasl_get_user(pn_sasl_t *sasl0) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + return sasl->username; +} + +const char *pn_sasl_get_mech(pn_sasl_t *sasl0) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + return sasl->selected_mechanism; +} + +void pn_sasl_allowed_mechs(pn_sasl_t *sasl0, const char *mechs) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + free(sasl->included_mechanisms); + sasl->included_mechanisms = mechs ? strdup(mechs) : NULL; + if (strcmp(mechs, "ANONYMOUS")==0 ) { + pn_transport_t *transport = get_transport_internal(sasl0); + pni_sasl_force_anonymous(transport); + } +} + +void pn_sasl_config_name(pn_sasl_t *sasl0, const char *name) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + sasl->config_name = 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 = strdup(dir); +} + +void pn_sasl_done(pn_sasl_t *sasl0, pn_sasl_outcome_t outcome) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + if (sasl) { + sasl->outcome = outcome; + } +} + +pn_sasl_outcome_t pn_sasl_outcome(pn_sasl_t *sasl0) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + return sasl ? sasl->outcome : PN_SASL_NONE; +} + +void pn_sasl_free(pn_transport_t *transport) +{ + if (transport) { + pni_sasl_t *sasl = transport->sasl; + if (sasl) { + free(sasl->selected_mechanism); + free(sasl->included_mechanisms); + free(sasl->password); + free(sasl->config_dir); + + // CYRUS_SASL + if (sasl->impl_context) { + pni_sasl_impl_free(transport); + } + + free(sasl); + } + } +} + +static void pni_sasl_server_init(pn_transport_t *transport) +{ + if (!pni_init_server(transport)) return; + + // Setup to send SASL mechanisms frame + pni_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS); +} + +static void pn_sasl_process(pn_transport_t *transport) +{ + pni_sasl_t *sasl = transport->sasl; + if (!sasl->client) { + if (sasl->desired_state<SASL_POSTED_MECHANISMS) { + pni_sasl_server_init(transport); + } + } +} + +ssize_t pn_sasl_input(pn_transport_t *transport, const char *bytes, size_t available) +{ + pn_sasl_process(transport); + + pni_sasl_t *sasl = transport->sasl; + ssize_t n = pn_dispatcher_input(transport, bytes, available, false, &sasl->halt); + + if (n==0 && pni_sasl_is_final_input_state(sasl)) { + return PN_EOS; + } + return n; +} + +ssize_t pn_sasl_output(pn_transport_t *transport, char *bytes, size_t size) +{ + pn_sasl_process(transport); + + pni_post_sasl_frame(transport); + + pni_sasl_t *sasl = transport->sasl; + if (transport->available == 0 && pni_sasl_is_final_output_state(sasl)) { + if (sasl->outcome != PN_SASL_OK && pni_sasl_is_final_input_state(sasl)) { + pn_transport_close_tail(transport); + } + return PN_EOS; + } else { + return pn_dispatcher_output(transport, bytes, size); + } +} + +// Received Server side +int pn_do_init(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +{ + pni_sasl_t *sasl = transport->sasl; + pn_bytes_t mech; + pn_bytes_t recv; + int err = pn_data_scan(args, "D.[sz]", &mech, &recv); + if (err) return err; + sasl->selected_mechanism = pn_strndup(mech.start, mech.size); + + pni_process_init(transport, sasl->selected_mechanism, &recv); + + return 0; +} + +// Received client side +int pn_do_mechanisms(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +{ + pni_sasl_t *sasl = transport->sasl; + + // If we already pretended we got the ANONYMOUS mech then ignore + if (sasl->last_state==SASL_PRETEND_OUTCOME) return 0; + + // This scanning relies on pn_data_scan leaving the pn_data_t cursors + // where they are after finishing the scan + int err = pn_data_scan(args, "D.[@["); + if (err) return err; + + pn_string_t *mechs = pn_string(""); + + // 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)) { + pn_string_addf(mechs, "%*s ", (int)s.size, s.start); + } + } + pn_string_buffer(mechs)[pn_string_size(mechs)-1] = 0; + + if (pni_init_client(transport) && + pni_process_mechanisms(transport, pn_string_get(mechs))) { + pni_sasl_set_desired_state(transport, SASL_POSTED_INIT); + } else { + sasl->last_state = SASL_RECVED_OUTCOME; + sasl->halt = true; + pn_transport_close_tail(transport); + } + + pn_free(mechs); + return 0; +} + +// Received client side +int pn_do_challenge(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +{ + pn_bytes_t recv; + int err = pn_data_scan(args, "D.[z]", &recv); + if (err) return err; + + pni_process_challenge(transport, &recv); + + return 0; +} + +// Received server side +int pn_do_response(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +{ + pn_bytes_t recv; + int err = pn_data_scan(args, "D.[z]", &recv); + if (err) return err; + + pni_process_response(transport, &recv); + + return 0; +} + +// Received client side +int pn_do_outcome(pn_transport_t *transport, uint8_t frame_type, uint16_t channel, pn_data_t *args, const pn_bytes_t *payload) +{ + uint8_t outcome; + int err = pn_data_scan(args, "D.[B]", &outcome); + if (err) return err; + + pni_sasl_t *sasl = transport->sasl; + sasl->outcome = (pn_sasl_outcome_t) outcome; + sasl->last_state = SASL_RECVED_OUTCOME; + sasl->halt = true; + + return 0; +} + + http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/df8e9ac5/proton-c/src/ssl/ssl-internal.h ---------------------------------------------------------------------- diff --git a/proton-c/src/ssl/ssl-internal.h b/proton-c/src/ssl/ssl-internal.h index f9687d0..3d9e163 100644 --- a/proton-c/src/ssl/ssl-internal.h +++ b/proton-c/src/ssl/ssl-internal.h @@ -21,6 +21,8 @@ * */ +#include "proton/ssl.h" + /** @file * Internal API for SSL/TLS support in the Driver Layer. * --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
