| /* |
| * |
| * 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 "remote_sasl.h" |
| |
| #include "server_private.h" |
| |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <proton/engine.h> |
| #include <proton/proactor.h> |
| #include <proton/sasl.h> |
| #include <proton/sasl_plugin.h> |
| #include <proton/object.h> |
| |
| #include "qpid/dispatch/ctools.h" |
| #include "qpid/dispatch/log.h" |
| |
| static qd_log_source_t* auth_service_log; |
| |
| typedef struct |
| { |
| size_t size; |
| char *start; |
| } qdr_owned_bytes_t; |
| |
| const int8_t UPSTREAM_INIT_RECEIVED = 1; |
| const int8_t UPSTREAM_RESPONSE_RECEIVED = 2; |
| const int8_t DOWNSTREAM_MECHANISMS_RECEIVED = 3; |
| const int8_t DOWNSTREAM_CHALLENGE_RECEIVED = 4; |
| const int8_t DOWNSTREAM_OUTCOME_RECEIVED = 5; |
| const int8_t DOWNSTREAM_CLOSED = 6; |
| |
| typedef struct { |
| size_t used; |
| size_t capacity; |
| char *start; |
| } buffer_t; |
| |
| |
| static void allocate_buffer(buffer_t* buffer) |
| { |
| buffer->start = qd_malloc(buffer->capacity); |
| memset(buffer->start, 0, buffer->capacity); |
| } |
| |
| static void free_buffer(buffer_t* buffer) |
| { |
| free(buffer->start); |
| buffer->start = 0; |
| buffer->capacity = 0; |
| buffer->used = 0; |
| } |
| |
| typedef struct { |
| buffer_t sources; |
| buffer_t targets; |
| } permissions_t; |
| |
| static void init_buffer(buffer_t* buffer) |
| { |
| buffer->used = 0; |
| buffer->capacity = 0; |
| buffer->start = 0; |
| } |
| |
| static void init_permissions(permissions_t* permissions) |
| { |
| init_buffer(&permissions->sources); |
| init_buffer(&permissions->targets); |
| } |
| |
| typedef struct |
| { |
| char* authentication_service_address; |
| char* hostname; |
| char* sasl_init_hostname; |
| pn_ssl_domain_t* ssl_domain; |
| pn_proactor_t* proactor; |
| |
| pn_connection_t* downstream; |
| char* selected_mechanism; |
| qdr_owned_bytes_t response; |
| int8_t downstream_state; |
| bool downstream_released; |
| |
| pn_connection_t* upstream; |
| char* mechlist; |
| qdr_owned_bytes_t challenge; |
| int8_t upstream_state; |
| bool upstream_released; |
| |
| bool complete; |
| char* username; |
| permissions_t permissions; |
| pn_sasl_outcome_t outcome; |
| |
| sys_mutex_t *lock; |
| } qdr_sasl_relay_t; |
| |
| static void copy_bytes(const pn_bytes_t* from, qdr_owned_bytes_t* to) |
| { |
| if (to->start) { |
| free(to->start); |
| } |
| to->start = (char*) malloc(from->size); |
| to->size = from->size; |
| memcpy(to->start, from->start, from->size); |
| } |
| |
| static qdr_sasl_relay_t* new_qdr_sasl_relay_t(const char* address, const char* hostname, const char* sasl_init_hostname, pn_proactor_t* proactor) |
| { |
| qdr_sasl_relay_t* instance = NEW(qdr_sasl_relay_t); |
| ZERO(instance); |
| instance->authentication_service_address = qd_strdup(address); |
| if (hostname) { |
| instance->hostname = qd_strdup(hostname); |
| } |
| if (sasl_init_hostname) { |
| instance->sasl_init_hostname = qd_strdup(sasl_init_hostname); |
| } |
| instance->proactor = proactor; |
| init_permissions(&instance->permissions); |
| instance->lock = sys_mutex(); |
| return instance; |
| } |
| |
| static void delete_qdr_sasl_relay_t(qdr_sasl_relay_t* instance) |
| { |
| if (instance->authentication_service_address) free(instance->authentication_service_address); |
| if (instance->hostname) free(instance->hostname); |
| if (instance->sasl_init_hostname) free(instance->sasl_init_hostname); |
| if (instance->ssl_domain) pn_ssl_domain_free(instance->ssl_domain); |
| if (instance->mechlist) free(instance->mechlist); |
| if (instance->selected_mechanism) free(instance->selected_mechanism); |
| if (instance->response.start) free(instance->response.start); |
| if (instance->challenge.start) free(instance->challenge.start); |
| if (instance->username) free(instance->username); |
| free_buffer(&(instance->permissions.targets)); |
| free_buffer(&(instance->permissions.sources)); |
| sys_mutex_free(instance->lock); |
| free(instance); |
| } |
| |
| PN_HANDLE(REMOTE_SASL_CTXT) |
| |
| bool qdr_is_authentication_service_connection(pn_connection_t* conn) |
| { |
| if (conn) { |
| pn_record_t *r = pn_connection_attachments(conn); |
| return pn_record_has(r, REMOTE_SASL_CTXT); |
| } else { |
| return false; |
| } |
| } |
| |
| static qdr_sasl_relay_t* get_sasl_relay_context(pn_connection_t* conn) |
| { |
| if (conn) { |
| pn_record_t *r = pn_connection_attachments(conn); |
| if (pn_record_has(r, REMOTE_SASL_CTXT)) { |
| return (qdr_sasl_relay_t*) pn_record_get(r, REMOTE_SASL_CTXT); |
| } else { |
| return NULL; |
| } |
| } else { |
| return NULL; |
| } |
| } |
| |
| static void set_sasl_relay_context(pn_connection_t* conn, qdr_sasl_relay_t* context) |
| { |
| pn_record_t *r = pn_connection_attachments(conn); |
| pn_record_def(r, REMOTE_SASL_CTXT, PN_VOID); |
| pn_record_set(r, REMOTE_SASL_CTXT, context); |
| } |
| |
| static bool remote_sasl_init_server(pn_transport_t* transport) |
| { |
| pn_connection_t* upstream = pn_transport_connection(transport); |
| if (upstream && pnx_sasl_get_context(transport)) { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl->upstream) return true; |
| impl->upstream = upstream; |
| pn_proactor_t* proactor = impl->proactor; |
| if (!proactor) return false; |
| impl->downstream = pn_connection(); |
| pn_connection_set_hostname(impl->downstream, impl->hostname); |
| set_sasl_relay_context(impl->downstream, impl); |
| //request permissions in response if supported by peer: |
| pn_data_t* data = pn_connection_desired_capabilities(impl->downstream); |
| pn_data_put_array(data, false, PN_SYMBOL); |
| pn_data_enter(data); |
| pn_data_put_symbol(data, pn_bytes(13, "ADDRESS-AUTHZ")); |
| pn_data_exit(data); |
| |
| data = pn_connection_properties(impl->downstream); |
| pn_data_put_map(data); |
| pn_data_enter(data); |
| pn_data_put_symbol(data, pn_bytes(strlen(QD_CONNECTION_PROPERTY_PRODUCT_KEY), QD_CONNECTION_PROPERTY_PRODUCT_KEY)); |
| pn_data_put_string(data, pn_bytes(strlen(QD_CONNECTION_PROPERTY_PRODUCT_VALUE), QD_CONNECTION_PROPERTY_PRODUCT_VALUE)); |
| pn_data_put_symbol(data, pn_bytes(strlen(QD_CONNECTION_PROPERTY_VERSION_KEY), QD_CONNECTION_PROPERTY_VERSION_KEY)); |
| pn_data_put_string(data, pn_bytes(strlen(QPID_DISPATCH_VERSION), QPID_DISPATCH_VERSION)); |
| pn_data_exit(data); |
| |
| pn_proactor_connect(proactor, impl->downstream, impl->authentication_service_address); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static bool remote_sasl_init_client(pn_transport_t* transport) |
| { |
| //for the client side of the connection to the authentication |
| //service, need to use the same context as the server side of the |
| //connection it is authenticating on behalf of |
| pn_connection_t* conn = pn_transport_connection(transport); |
| qdr_sasl_relay_t* impl = get_sasl_relay_context(conn); |
| if (impl) { |
| pnx_sasl_set_context(transport, impl); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| |
| static void connection_wake(pn_connection_t* conn) |
| { |
| qd_connection_t *ctx = pn_connection_get_context(conn); |
| if (ctx) { |
| ctx->wake(ctx); |
| } else { |
| pn_connection_wake(conn); |
| } |
| } |
| |
| static bool notify_upstream(qdr_sasl_relay_t* impl, uint8_t state) |
| { |
| if (!impl->upstream_released) { |
| impl->upstream_state = state; |
| connection_wake(impl->upstream); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static bool notify_downstream(qdr_sasl_relay_t* impl, uint8_t state) |
| { |
| if (!impl->downstream_released && impl->downstream) { |
| impl->downstream_state = state; |
| connection_wake(impl->downstream); |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static bool delete_on_downstream_freed(qdr_sasl_relay_t* impl) |
| { |
| bool result; |
| sys_mutex_lock(impl->lock); |
| impl->downstream_released = true; |
| result = impl->upstream_released; |
| sys_mutex_unlock(impl->lock); |
| return result; |
| } |
| |
| static bool delete_on_upstream_freed(qdr_sasl_relay_t* impl) |
| { |
| bool result; |
| sys_mutex_lock(impl->lock); |
| impl->upstream_released = true; |
| result = impl->downstream_released || impl->downstream == 0; |
| sys_mutex_unlock(impl->lock); |
| return result; |
| } |
| |
| static bool can_delete(pn_transport_t *transport, qdr_sasl_relay_t* impl) |
| { |
| if (pnx_sasl_is_client(transport)) { |
| return delete_on_downstream_freed(impl); |
| } else { |
| return delete_on_upstream_freed(impl); |
| } |
| } |
| |
| static void remote_sasl_free(pn_transport_t *transport) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl && can_delete(transport, impl)) { |
| delete_qdr_sasl_relay_t(impl); |
| } |
| } |
| |
| static void set_policy_settings(pn_connection_t* conn, permissions_t* permissions) |
| { |
| if (permissions->targets.start || permissions->sources.start) { |
| qd_connection_t *qd_conn = (qd_connection_t*) pn_connection_get_context(conn); |
| qd_conn->policy_settings = new_qd_policy_settings_t(); |
| ZERO(qd_conn->policy_settings); |
| |
| if (permissions->targets.start && permissions->targets.capacity) { |
| qd_conn->policy_settings->targets = qd_policy_compile_allowed_csv(permissions->targets.start); |
| } |
| if (permissions->sources.start && permissions->sources.capacity) { |
| qd_conn->policy_settings->sources = qd_policy_compile_allowed_csv(permissions->sources.start); |
| } |
| qd_conn->policy_settings->spec.allowDynamicSource = true; |
| qd_conn->policy_settings->spec.allowAnonymousSender = true; |
| } |
| } |
| |
| static void remote_sasl_prepare(pn_transport_t *transport) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (!impl) return; |
| if (pnx_sasl_is_client(transport)) { |
| if (impl->downstream_state == UPSTREAM_INIT_RECEIVED) { |
| pnx_sasl_set_selected_mechanism(transport, impl->selected_mechanism); |
| pnx_sasl_set_local_hostname(transport, impl->sasl_init_hostname); |
| pnx_sasl_set_bytes_out(transport, pn_bytes(impl->response.size, impl->response.start)); |
| pnx_sasl_set_desired_state(transport, SASL_POSTED_INIT); |
| } else if (impl->downstream_state == UPSTREAM_RESPONSE_RECEIVED) { |
| pnx_sasl_set_bytes_out(transport, pn_bytes(impl->response.size, impl->response.start)); |
| pnx_sasl_set_desired_state(transport, SASL_POSTED_RESPONSE); |
| } |
| impl->downstream_state = 0; |
| } else { |
| if (impl->upstream_state == DOWNSTREAM_MECHANISMS_RECEIVED) { |
| pnx_sasl_set_desired_state(transport, SASL_POSTED_MECHANISMS); |
| } else if (impl->upstream_state == DOWNSTREAM_CHALLENGE_RECEIVED) { |
| pnx_sasl_set_bytes_out(transport, pn_bytes(impl->challenge.size, impl->challenge.start)); |
| pnx_sasl_set_desired_state(transport, SASL_POSTED_CHALLENGE); |
| } else if (impl->upstream_state == DOWNSTREAM_OUTCOME_RECEIVED) { |
| switch (impl->outcome) { |
| case PN_SASL_OK: |
| set_policy_settings(impl->upstream, &impl->permissions); |
| qd_log(auth_service_log, QD_LOG_INFO, "authenticated as %s", impl->username); |
| pnx_sasl_set_succeeded(transport, impl->username, NULL); |
| break; |
| default: |
| pnx_sasl_set_failed(transport); |
| } |
| pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); |
| } else if (impl->upstream_state == DOWNSTREAM_CLOSED) { |
| pnx_sasl_set_failed(transport); |
| pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); |
| } |
| impl->upstream_state = 0; |
| } |
| } |
| |
| // Client / Downstream |
| static bool remote_sasl_process_mechanisms(pn_transport_t *transport, const char *mechs) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl) { |
| impl->mechlist = qd_strdup(mechs); |
| if (notify_upstream(impl, DOWNSTREAM_MECHANISMS_RECEIVED)) { |
| return true; |
| } else { |
| pnx_sasl_set_desired_state(transport, SASL_ERROR); |
| return false; |
| } |
| } else { |
| return false; |
| } |
| } |
| |
| // Client / Downstream |
| static void remote_sasl_process_challenge(pn_transport_t *transport, const pn_bytes_t *recv) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl) { |
| copy_bytes(recv, &(impl->challenge)); |
| if (!notify_upstream(impl, DOWNSTREAM_CHALLENGE_RECEIVED)) { |
| pnx_sasl_set_desired_state(transport, SASL_ERROR); |
| } |
| } |
| } |
| |
| // Client / Downstream |
| static void remote_sasl_process_outcome(pn_transport_t *transport, const pn_bytes_t *recv) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl) { |
| pn_sasl_t* sasl = pn_sasl(transport); |
| if (sasl) { |
| impl->outcome = pn_sasl_outcome(sasl); |
| impl->complete = true; |
| //only consider complete if failed; if successful wait for the open frame |
| if (impl->outcome != PN_SASL_OK && !notify_upstream(impl, DOWNSTREAM_OUTCOME_RECEIVED)) { |
| pnx_sasl_set_desired_state(transport, SASL_ERROR); |
| pn_transport_close_tail(transport); |
| pn_transport_close_head(transport); |
| } |
| } |
| } |
| } |
| |
| // Server / Upstream |
| static const char* remote_sasl_list_mechs(pn_transport_t *transport) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl && impl->mechlist) { |
| return impl->mechlist; |
| } else { |
| return NULL; |
| } |
| } |
| |
| // Server / Upstream |
| static void remote_sasl_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *recv) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl) { |
| impl->selected_mechanism = qd_strdup(mechanism); |
| copy_bytes(recv, &(impl->response)); |
| if (!notify_downstream(impl, UPSTREAM_INIT_RECEIVED)) { |
| pnx_sasl_set_desired_state(transport, SASL_ERROR); |
| } |
| } |
| } |
| |
| // Server / Upstream |
| static void remote_sasl_process_response(pn_transport_t *transport, const pn_bytes_t *recv) |
| { |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (impl) { |
| copy_bytes(recv, &(impl->response)); |
| if (!notify_downstream(impl, UPSTREAM_RESPONSE_RECEIVED)) { |
| pnx_sasl_set_desired_state(transport, SASL_ERROR); |
| } |
| } |
| } |
| |
| static bool remote_sasl_can_encrypt(pn_transport_t *transport) |
| { |
| return false; |
| } |
| |
| static ssize_t remote_sasl_max_encrypt_size(pn_transport_t *transport) |
| { |
| return 0; |
| } |
| static ssize_t remote_sasl_encode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out) |
| { |
| return 0; |
| } |
| static ssize_t remote_sasl_decode(pn_transport_t *transport, pn_bytes_t in, pn_bytes_t *out) |
| { |
| return 0; |
| } |
| |
| |
| static const pnx_sasl_implementation remote_sasl_impl = { |
| remote_sasl_free, |
| remote_sasl_list_mechs, |
| remote_sasl_init_server, |
| remote_sasl_init_client, |
| remote_sasl_prepare, |
| remote_sasl_process_init, |
| remote_sasl_process_response, |
| remote_sasl_process_mechanisms, |
| remote_sasl_process_challenge, |
| remote_sasl_process_outcome, |
| remote_sasl_can_encrypt, |
| remote_sasl_max_encrypt_size, |
| remote_sasl_encode, |
| remote_sasl_decode |
| }; |
| |
| static void set_remote_impl(pn_transport_t *transport, qdr_sasl_relay_t* context) |
| { |
| pnx_sasl_set_implementation(transport, &remote_sasl_impl, context); |
| } |
| |
| void qdr_use_remote_authentication_service(pn_transport_t *transport, const char* address, const char* hostname, const char* sasl_init_hostname, pn_ssl_domain_t* ssl_domain, pn_proactor_t* proactor) |
| { |
| auth_service_log = qd_log_source("AUTHSERVICE"); |
| qdr_sasl_relay_t* context = new_qdr_sasl_relay_t(address, hostname, sasl_init_hostname, proactor); |
| context->ssl_domain = ssl_domain; |
| set_remote_impl(transport, context); |
| } |
| |
| static bool append(buffer_t* buffer, pn_bytes_t data) |
| { |
| if (buffer->capacity > data.size + buffer->used) { |
| if (buffer->used > 0) buffer->start[buffer->used++] = ','; |
| strncpy(buffer->start + buffer->used, data.start, data.size); |
| buffer->used += data.size; |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| static size_t min(size_t a, size_t b) |
| { |
| if (a > b) return b; |
| else return a; |
| } |
| |
| typedef void* (*permission_handler)(pn_bytes_t, bool, bool, void*); |
| |
| static void* compute_required_size(pn_bytes_t address, bool send, bool recv, void* context) |
| { |
| permissions_t* permissions = (permissions_t*) context; |
| if (send) permissions->targets.capacity += address.size + 1; |
| if (recv) permissions->sources.capacity += address.size + 1; |
| return context; |
| } |
| |
| static void* collect_permissions(pn_bytes_t address, bool send, bool recv, void* context) |
| { |
| permissions_t* permissions = (permissions_t*) context; |
| if (send) append(&(permissions->targets), address); |
| if (recv) append(&(permissions->sources), address); |
| return context; |
| } |
| |
| static void* parse_permissions(pn_data_t* data, permission_handler handler, void* initial_context) |
| { |
| void* context = initial_context; |
| size_t count = pn_data_get_map(data); |
| pn_data_enter(data); |
| for (size_t i = 0; i < count/2; i++) { |
| if (pn_data_next(data)) { |
| if (pn_data_type(data) == PN_STRING) { |
| pn_bytes_t address = pn_data_get_string(data); |
| if (pn_data_next(data)) { |
| if (pn_data_type(data) == PN_ARRAY && pn_data_get_array_type(data) == PN_STRING) { |
| size_t length = pn_data_get_array(data); |
| pn_data_enter(data); |
| for (size_t j = 0; j < length; j++) { |
| if (pn_data_next(data)) { |
| pn_bytes_t permission = pn_data_get_string(data); |
| //printf("in permissions map %i of %i is %.*s for %.*s\n", (int) (j+1), (int) length, (int) permission.size, permission.start, (int) address.size, address.start); |
| bool send = strncmp(permission.start, "send", min(permission.size, 4)) == 0; |
| bool recv = strncmp(permission.start, "recv", min(permission.size, 4)) == 0; |
| |
| if (send || recv) { |
| context = handler(address, send, recv, context); |
| } |
| } |
| } |
| pn_data_exit(data); |
| } |
| } |
| } else { |
| //key is not string, consume value to move onto next pair |
| pn_data_next(data); |
| } |
| } |
| } |
| pn_data_exit(data); |
| return context; |
| } |
| |
| static void* parse_properties(pn_data_t* data, permission_handler handler, void* initial_context) |
| { |
| void* context = 0; |
| size_t count = pn_data_get_map(data); |
| pn_data_enter(data); |
| for (size_t i = 0; !context && i < count/2; i++) { |
| if (pn_data_next(data)) { |
| if (pn_data_type(data) == PN_SYMBOL) { |
| pn_bytes_t key = pn_data_get_symbol(data); |
| if (key.size && key.start && strncmp(key.start, "address-authz", min(key.size, 13)) == 0) { |
| pn_data_next(data); |
| context = parse_permissions(data, handler, initial_context); |
| } else { |
| //key didn't match, move to next pair |
| pn_data_next(data); |
| } |
| } else { |
| //key was not symbol, move to next pair |
| pn_data_next(data); |
| } |
| } |
| } |
| pn_data_exit(data); |
| pn_data_rewind(data); |
| pn_data_next(data); |
| return context; |
| } |
| |
| static pn_data_t* extract_map_entry(pn_data_t* data, const char* name) |
| { |
| pn_data_t* result = 0; |
| size_t count = pn_data_get_map(data); |
| pn_data_enter(data); |
| for (size_t i = 0; !result && i < count/2; i++) { |
| if (pn_data_next(data)) { |
| if (pn_data_type(data) == PN_SYMBOL || pn_data_type(data) == PN_STRING) { |
| pn_bytes_t key = pn_data_type(data) == PN_SYMBOL ? pn_data_get_symbol(data) : pn_data_get_string(data); |
| if (key.size && key.start && strncmp(key.start, name, min(key.size, strlen(name))) == 0) { |
| pn_data_next(data); |
| result = data; |
| } else { |
| //key didn't match, move to next pair |
| pn_data_next(data); |
| } |
| } else { |
| //key was not symbol, move to next pair |
| pn_data_next(data); |
| } |
| } |
| } |
| return result; |
| } |
| |
| static pn_bytes_t extract_authenticated_identity(pn_data_t* data) |
| { |
| pn_bytes_t result = pn_bytes_null; |
| pn_data_t* authid = extract_map_entry(data, "authenticated-identity"); |
| if (authid) { |
| pn_data_t* id = extract_map_entry(authid, "sub"); |
| if (id) { |
| result = pn_data_get_string(id); |
| } |
| pn_data_exit(data); |
| } |
| pn_data_exit(data); |
| pn_data_rewind(data); |
| pn_data_next(data); |
| return result; |
| } |
| |
| void qdr_handle_authentication_service_connection_event(pn_event_t *e) |
| { |
| pn_connection_t *conn = pn_event_connection(e); |
| pn_transport_t *transport = pn_event_transport(e); |
| if (pn_event_type(e) == PN_CONNECTION_BOUND) { |
| pn_sasl(transport); |
| qd_log(auth_service_log, QD_LOG_DEBUG, "Handling connection bound event for authentication service connection"); |
| qdr_sasl_relay_t* context = get_sasl_relay_context(conn); |
| if (context->ssl_domain) { |
| pn_ssl_t* ssl = pn_ssl(transport); |
| if (!ssl || pn_ssl_init(ssl, context->ssl_domain, 0)) { |
| qd_log(auth_service_log, QD_LOG_WARNING, "Cannot initialise SSL"); |
| } else { |
| qd_log(auth_service_log, QD_LOG_DEBUG, "Successfully initialised SSL"); |
| } |
| } |
| set_remote_impl(pn_event_transport(e), context); |
| } else if (pn_event_type(e) == PN_CONNECTION_REMOTE_OPEN) { |
| qd_log(auth_service_log, QD_LOG_DEBUG, "authentication against service complete; closing connection"); |
| |
| qdr_sasl_relay_t* context = get_sasl_relay_context(conn); |
| //extract permissions as two comma separated lists (allowed sources and targets) |
| pn_data_t* properties = pn_connection_remote_properties(conn); |
| if (parse_properties(properties, compute_required_size, (void*) &(context->permissions))) { |
| if (!context->permissions.sources.capacity) { |
| context->permissions.sources.capacity = 1; |
| } |
| if (!context->permissions.targets.capacity) { |
| context->permissions.targets.capacity = 1; |
| } |
| allocate_buffer(&(context->permissions.targets)); |
| allocate_buffer(&(context->permissions.sources)); |
| parse_properties(properties, collect_permissions, (void*) &(context->permissions)); |
| } |
| const pn_bytes_t authid = extract_authenticated_identity(properties); |
| if (authid.start && authid.size) { |
| context->username = strndup(authid.start, authid.size); |
| } else { |
| context->username = qd_strdup(""); |
| } |
| //notify upstream connection of successful authentication |
| notify_upstream(context, DOWNSTREAM_OUTCOME_RECEIVED); |
| |
| //close downstream connection |
| pn_connection_close(conn); |
| pn_transport_close_tail(transport); |
| pn_transport_close_head(transport); |
| } else if (pn_event_type(e) == PN_CONNECTION_REMOTE_CLOSE) { |
| qd_log(auth_service_log, QD_LOG_DEBUG, "authentication service closed connection"); |
| pn_connection_close(conn); |
| pn_transport_close_head(transport); |
| } else if (pn_event_type(e) == PN_TRANSPORT_HEAD_CLOSED) { |
| pn_transport_close_tail(transport); |
| } else if (pn_event_type(e) == PN_TRANSPORT_TAIL_CLOSED) { |
| pn_transport_close_head(transport); |
| } else if (pn_event_type(e) == PN_TRANSPORT_CLOSED) { |
| qd_log(auth_service_log, QD_LOG_DEBUG, "disconnected from authentication service"); |
| qdr_sasl_relay_t* impl = (qdr_sasl_relay_t*) pnx_sasl_get_context(transport); |
| if (!impl->complete) { |
| notify_upstream(impl, DOWNSTREAM_CLOSED); |
| pn_condition_t* condition = pn_transport_condition(transport); |
| if (condition) { |
| qd_log(auth_service_log, QD_LOG_WARNING, "Downstream disconnected: %s %s", pn_condition_get_name(condition), pn_condition_get_description(condition)); |
| } else { |
| qd_log(auth_service_log, QD_LOG_WARNING, "Downstream disconnected, no details available"); |
| } |
| } |
| } else { |
| qd_log(auth_service_log, QD_LOG_DEBUG, "Ignoring event for authentication service connection: %s", pn_event_type_name(pn_event_type(e))); |
| } |
| } |