From commits-return-44710-archive-asf-public=cust-asf.ponee.io@qpid.apache.org Thu Apr 5 21:33:45 2018 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by mx-eu-01.ponee.io (Postfix) with SMTP id 4B6DE1807D2 for ; Thu, 5 Apr 2018 21:33:40 +0200 (CEST) Received: (qmail 13621 invoked by uid 500); 5 Apr 2018 19:33:39 -0000 Mailing-List: contact commits-help@qpid.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@qpid.apache.org Delivered-To: mailing list commits@qpid.apache.org Received: (qmail 11898 invoked by uid 99); 5 Apr 2018 19:33:38 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Thu, 05 Apr 2018 19:33:38 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 17E4EF6BCD; Thu, 5 Apr 2018 19:33:37 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: jross@apache.org To: commits@qpid.apache.org Date: Thu, 05 Apr 2018 19:34:04 -0000 Message-Id: <8ef3e488e5ac4ce88d48469f10bc7ec7@git.apache.org> In-Reply-To: <20ae71952c0b49a196f394a6bdd04e46@git.apache.org> References: <20ae71952c0b49a196f394a6bdd04e46@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [29/51] [partial] qpid-proton git commit: PROTON-1728: Reorganize the source tree http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/reactor/selectable.c ---------------------------------------------------------------------- diff --git a/c/src/reactor/selectable.c b/c/src/reactor/selectable.c new file mode 100644 index 0000000..b42ad1f --- /dev/null +++ b/c/src/reactor/selectable.c @@ -0,0 +1,300 @@ +/* + * + * 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 "selectable.h" + +#include + +#include "io.h" + +#include +#include + +pn_selectables_t *pn_selectables(void) +{ + return pn_iterator(); +} + +pn_selectable_t *pn_selectables_next(pn_selectables_t *selectables) +{ + return (pn_selectable_t *) pn_iterator_next(selectables); +} + +void pn_selectables_free(pn_selectables_t *selectables) +{ + pn_free(selectables); +} + +struct pn_selectable_t { + pn_socket_t fd; + int index; + pn_record_t *attachments; + void (*readable)(pn_selectable_t *); + void (*writable)(pn_selectable_t *); + void (*error)(pn_selectable_t *); + void (*expired)(pn_selectable_t *); + void (*release) (pn_selectable_t *); + void (*finalize)(pn_selectable_t *); + pn_collector_t *collector; + pn_timestamp_t deadline; + bool reading; + bool writing; + bool registered; + bool terminal; +}; + +void pn_selectable_initialize(pn_selectable_t *sel) +{ + sel->fd = PN_INVALID_SOCKET; + sel->index = -1; + sel->attachments = pn_record(); + sel->readable = NULL; + sel->writable = NULL; + sel->error = NULL; + sel->expired = NULL; + sel->release = NULL; + sel->finalize = NULL; + sel->collector = NULL; + sel->deadline = 0; + sel->reading = false; + sel->writing = false; + sel->registered = false; + sel->terminal = false; +} + +void pn_selectable_finalize(pn_selectable_t *sel) +{ + if (sel->finalize) { + sel->finalize(sel); + } + pn_decref(sel->attachments); + pn_decref(sel->collector); +} + +#define pn_selectable_hashcode NULL +#define pn_selectable_inspect NULL +#define pn_selectable_compare NULL + +PN_CLASSDEF(pn_selectable) + +pn_selectable_t *pn_selectable(void) +{ + return pn_selectable_new(); +} + +bool pn_selectable_is_reading(pn_selectable_t *sel) { + assert(sel); + return sel->reading; +} + +void pn_selectable_set_reading(pn_selectable_t *sel, bool reading) { + assert(sel); + sel->reading = reading; +} + +bool pn_selectable_is_writing(pn_selectable_t *sel) { + assert(sel); + return sel->writing; +} + +void pn_selectable_set_writing(pn_selectable_t *sel, bool writing) { + assert(sel); + sel->writing = writing; +} + +pn_timestamp_t pn_selectable_get_deadline(pn_selectable_t *sel) { + assert(sel); + return sel->deadline; +} + +void pn_selectable_set_deadline(pn_selectable_t *sel, pn_timestamp_t deadline) { + assert(sel); + sel->deadline = deadline; +} + +void pn_selectable_on_readable(pn_selectable_t *sel, void (*readable)(pn_selectable_t *)) { + assert(sel); + sel->readable = readable; +} + +void pn_selectable_on_writable(pn_selectable_t *sel, void (*writable)(pn_selectable_t *)) { + assert(sel); + sel->writable = writable; +} + +void pn_selectable_on_error(pn_selectable_t *sel, void (*error)(pn_selectable_t *)) { + assert(sel); + sel->error = error; +} + +void pn_selectable_on_expired(pn_selectable_t *sel, void (*expired)(pn_selectable_t *)) { + assert(sel); + sel->expired = expired; +} + +void pn_selectable_on_release(pn_selectable_t *sel, void (*release)(pn_selectable_t *)) { + assert(sel); + sel->release = release; +} + +void pn_selectable_on_finalize(pn_selectable_t *sel, void (*finalize)(pn_selectable_t *)) { + assert(sel); + sel->finalize = finalize; +} + +pn_record_t *pn_selectable_attachments(pn_selectable_t *sel) { + return sel->attachments; +} + +void *pni_selectable_get_context(pn_selectable_t *selectable) +{ + assert(selectable); + return pn_record_get(selectable->attachments, PN_LEGCTX); +} + +void pni_selectable_set_context(pn_selectable_t *selectable, void *context) +{ + assert(selectable); + pn_record_set(selectable->attachments, PN_LEGCTX, context); +} + +int pni_selectable_get_index(pn_selectable_t *selectable) +{ + assert(selectable); + return selectable->index; +} + +void pni_selectable_set_index(pn_selectable_t *selectable, int index) +{ + assert(selectable); + selectable->index = index; +} + +pn_socket_t pn_selectable_get_fd(pn_selectable_t *selectable) +{ + assert(selectable); + return selectable->fd; +} + +void pn_selectable_set_fd(pn_selectable_t *selectable, pn_socket_t fd) +{ + assert(selectable); + selectable->fd = fd; +} + +void pn_selectable_readable(pn_selectable_t *selectable) +{ + assert(selectable); + if (selectable->readable) { + selectable->readable(selectable); + } +} + +void pn_selectable_writable(pn_selectable_t *selectable) +{ + assert(selectable); + if (selectable->writable) { + selectable->writable(selectable); + } +} + +void pn_selectable_error(pn_selectable_t *selectable) +{ + assert(selectable); + if (selectable->error) { + selectable->error(selectable); + } +} + +void pn_selectable_expired(pn_selectable_t *selectable) +{ + assert(selectable); + if (selectable->expired) { + selectable->expired(selectable); + } +} + +bool pn_selectable_is_registered(pn_selectable_t *selectable) +{ + assert(selectable); + return selectable->registered; +} + +void pn_selectable_set_registered(pn_selectable_t *selectable, bool registered) +{ + assert(selectable); + selectable->registered = registered; +} + +bool pn_selectable_is_terminal(pn_selectable_t *selectable) +{ + assert(selectable); + return selectable->terminal; +} + +void pn_selectable_terminate(pn_selectable_t *selectable) +{ + assert(selectable); + selectable->terminal = true; +} + +void pn_selectable_release(pn_selectable_t *selectable) +{ + assert(selectable); + if (selectable->release) { + selectable->release(selectable); + } +} + +void pn_selectable_free(pn_selectable_t *selectable) +{ + pn_decref(selectable); +} + +static void pni_readable(pn_selectable_t *selectable) { + pn_collector_put(selectable->collector, PN_OBJECT, selectable, PN_SELECTABLE_READABLE); +} + +static void pni_writable(pn_selectable_t *selectable) { + pn_collector_put(selectable->collector, PN_OBJECT, selectable, PN_SELECTABLE_WRITABLE); +} + +static void pni_error(pn_selectable_t *selectable) { + pn_collector_put(selectable->collector, PN_OBJECT, selectable, PN_SELECTABLE_ERROR); +} + +static void pni_expired(pn_selectable_t *selectable) { + pn_collector_put(selectable->collector, PN_OBJECT, selectable, PN_SELECTABLE_EXPIRED); +} + +void pn_selectable_collect(pn_selectable_t *selectable, pn_collector_t *collector) { + assert(selectable); + pn_decref(selectable->collector); + selectable->collector = collector; + pn_incref(selectable->collector); + + if (collector) { + pn_selectable_on_readable(selectable, pni_readable); + pn_selectable_on_writable(selectable, pni_writable); + pn_selectable_on_error(selectable, pni_error); + pn_selectable_on_expired(selectable, pni_expired); + } +} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/reactor/selectable.h ---------------------------------------------------------------------- diff --git a/c/src/reactor/selectable.h b/c/src/reactor/selectable.h new file mode 100644 index 0000000..7a5b80b --- /dev/null +++ b/c/src/reactor/selectable.h @@ -0,0 +1,36 @@ +#ifndef _PROTON_SRC_SELECTABLE_H +#define _PROTON_SRC_SELECTABLE_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. + * + */ + +#ifndef __cplusplus +#include +#endif + +#include + +void *pni_selectable_get_context(pn_selectable_t *selectable); +void pni_selectable_set_context(pn_selectable_t *selectable, void *context); +int pni_selectable_get_index(pn_selectable_t *selectable); +void pni_selectable_set_index(pn_selectable_t *selectable, int index); + +#endif /* selectable.h */ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/reactor/selector.h ---------------------------------------------------------------------- diff --git a/c/src/reactor/selector.h b/c/src/reactor/selector.h new file mode 100644 index 0000000..2a1e31f --- /dev/null +++ b/c/src/reactor/selector.h @@ -0,0 +1,53 @@ +#ifndef PROTON_SELECTOR_H +#define PROTON_SELECTOR_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 +#include + +#define PN_READABLE (1) +#define PN_WRITABLE (2) +#define PN_EXPIRED (4) +#define PN_ERROR (8) + +/** + * A ::pn_selector_t provides a selection mechanism that allows + * efficient monitoring of a large number of Proton connections and + * listeners. + * + * External (non-Proton) sockets may also be monitored, either solely + * for event notification (read, write, and timer) or event + * notification and use with pn_io_t interfaces. + */ +typedef struct pn_selector_t pn_selector_t; + +pn_selector_t *pni_selector(void); +void pn_selector_free(pn_selector_t *selector); +void pn_selector_add(pn_selector_t *selector, pn_selectable_t *selectable); +void pn_selector_update(pn_selector_t *selector, pn_selectable_t *selectable); +void pn_selector_remove(pn_selector_t *selector, pn_selectable_t *selectable); +size_t pn_selector_size(pn_selector_t *selector); +int pn_selector_select(pn_selector_t *select, int timeout); +pn_selectable_t *pn_selector_next(pn_selector_t *select, int *events); + +#endif /* selector.h */ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/reactor/timer.c ---------------------------------------------------------------------- diff --git a/c/src/reactor/timer.c b/c/src/reactor/timer.c new file mode 100644 index 0000000..bfd768e --- /dev/null +++ b/c/src/reactor/timer.c @@ -0,0 +1,168 @@ +/* + * + * 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 +#include +#include + +struct pn_task_t { + pn_list_t *pool; + pn_record_t *attachments; + pn_timestamp_t deadline; + bool cancelled; +}; + +void pn_task_initialize(pn_task_t *task) { + task->pool = NULL; + task->attachments = pn_record(); + task->deadline = 0; + task->cancelled = false; +} + +void pn_task_finalize(pn_task_t *task) { + // if we are the last reference to the pool then don't put ourselves + // into it + if (task->pool && pn_refcount(task->pool) > 1) { + pn_record_clear(task->attachments); + pn_list_add(task->pool, task); + pn_decref(task->pool); + task->pool = NULL; + } else { + pn_decref(task->pool); + pn_decref(task->attachments); + } +} + +intptr_t pn_task_compare(pn_task_t *a, pn_task_t *b) { + return a->deadline - b->deadline; +} + +#define pn_task_inspect NULL +#define pn_task_hashcode NULL + +PN_CLASSDEF(pn_task) + +pn_task_t *pn_task(void) { + pn_task_t *task = pn_task_new(); + return task; +} + +pn_record_t *pn_task_attachments(pn_task_t *task) { + assert(task); + return task->attachments; +} + +void pn_task_cancel(pn_task_t *task) { + assert(task); + task->cancelled = true; +} + +// +// timer +// + +struct pn_timer_t { + pn_list_t *pool; + pn_list_t *tasks; + pn_collector_t *collector; +}; + +static void pn_timer_initialize(pn_timer_t *timer) { + timer->pool = pn_list(PN_OBJECT, 0); + timer->tasks = pn_list(PN_OBJECT, 0); +} + +static void pn_timer_finalize(pn_timer_t *timer) { + pn_decref(timer->pool); + pn_free(timer->tasks); +} + +#define pn_timer_inspect NULL +#define pn_timer_compare NULL +#define pn_timer_hashcode NULL + +PN_CLASSDEF(pn_timer) + +pn_timer_t *pn_timer(pn_collector_t *collector) { + pn_timer_t *timer = pn_timer_new(); + timer->collector = collector; + return timer; +} + +pn_task_t *pn_timer_schedule(pn_timer_t *timer, pn_timestamp_t deadline) { + pn_task_t *task = (pn_task_t *) pn_list_pop(timer->pool); + if (!task) { + task = pn_task(); + } + task->pool = timer->pool; + pn_incref(task->pool); + task->deadline = deadline; + task->cancelled = false; + pn_list_minpush(timer->tasks, task); + pn_decref(task); + return task; +} + +void pni_timer_flush_cancelled(pn_timer_t *timer) { + while (pn_list_size(timer->tasks)) { + pn_task_t *task = (pn_task_t *) pn_list_get(timer->tasks, 0); + if (task->cancelled) { + pn_task_t *min = (pn_task_t *) pn_list_minpop(timer->tasks); + assert(min == task); + pn_decref(min); + } else { + break; + } + } +} + +pn_timestamp_t pn_timer_deadline(pn_timer_t *timer) { + assert(timer); + pni_timer_flush_cancelled(timer); + if (pn_list_size(timer->tasks)) { + pn_task_t *task = (pn_task_t *) pn_list_get(timer->tasks, 0); + return task->deadline; + } else { + return 0; + } +} + +void pn_timer_tick(pn_timer_t *timer, pn_timestamp_t now) { + assert(timer); + while (pn_list_size(timer->tasks)) { + pn_task_t *task = (pn_task_t *) pn_list_get(timer->tasks, 0); + if (now >= task->deadline) { + pn_task_t *min = (pn_task_t *) pn_list_minpop(timer->tasks); + assert(min == task); + if (!min->cancelled) + pn_collector_put(timer->collector, PN_OBJECT, min, PN_TIMER_TASK); + pn_decref(min); + } else { + break; + } + } +} + +int pn_timer_tasks(pn_timer_t *timer) { + assert(timer); + pni_timer_flush_cancelled(timer); + return pn_list_size(timer->tasks); +} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/sasl/cyrus_sasl.c ---------------------------------------------------------------------- diff --git a/c/src/sasl/cyrus_sasl.c b/c/src/sasl/cyrus_sasl.c new file mode 100644 index 0000000..c589fab --- /dev/null +++ b/c/src/sasl/cyrus_sasl.c @@ -0,0 +1,605 @@ +/* + * + * 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. + * + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include "proton/sasl.h" +#include "proton/sasl-plugin.h" +#include "proton/transport.h" + +#include +#include +#include +#include +#include + +#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 +// be called twice. +#if SASL_VERSION_FULL<0x020118 +# define sasl_client_done() +# define sasl_server_done() +#endif + +static const char *amqp_service = "amqp"; + +static inline bool pni_check_result(sasl_conn_t *conn, int r, pn_transport_t *logger, const char* condition_name) +{ + if (r==SASL_OK) return true; + + const char* err = conn ? sasl_errdetail(conn) : sasl_errstring(r, NULL, NULL); + pnx_sasl_logf(logger, "sasl error: %s", err); + pn_condition_t* c = pn_transport_condition(logger); + pn_condition_set_name(c, condition_name); + pn_condition_set_description(c, err); + return false; +} + +static bool pni_check_io_result(sasl_conn_t *conn, int r, pn_transport_t *logger) +{ + return pni_check_result(conn, r, logger, "proton:io:sasl_error"); +} + +static bool pni_check_sasl_result(sasl_conn_t *conn, int r, pn_transport_t *logger) +{ + return pni_check_result(conn, r, logger, "amqp:unauthorized-access"); +} + +// Cyrus wrappers +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) { + case SASL_CB_USER: + i->result = 0; + i->len = 0; + break; + case SASL_CB_AUTHNAME: { + const char *username = pnx_sasl_get_username(transport); + i->result = username; + i->len = strlen(username); + break; + } + 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); + } + } +} + +const char *cyrus_sasl_list_mechs(pn_transport_t *transport) +{ + sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport); + if (!cyrus_conn) return NULL; + + int count = 0; + 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 +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}, +}; + +static const sasl_callback_t pni_user_callbacks[] = { + {SASL_CB_USER, NULL, NULL}, + {SASL_CB_AUTHNAME, NULL, NULL}, + {SASL_CB_LIST_END, NULL, NULL}, +}; + +// Machinery to initialise the cyrus library only once even in a multithreaded environment +// Relies on pthreads. +static const char * const default_config_name = "proton-server"; +static char *pni_cyrus_config_dir = NULL; +static char *pni_cyrus_config_name = NULL; +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); + if (pni_cyrus_client_started) sasl_client_done(); + if (pni_cyrus_server_started) sasl_server_done(); + free(pni_cyrus_config_dir); + free(pni_cyrus_config_name); + pthread_mutex_unlock(&pni_cyrus_mutex); +} + +static int pni_cyrus_client_init_rc = SASL_OK; +static void pni_cyrus_client_once(void) { + pthread_mutex_lock(&pni_cyrus_mutex); + 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); + } + pni_cyrus_client_started = true; + pni_cyrus_client_init_rc = result; + pthread_mutex_unlock(&pni_cyrus_mutex); +} + +static int pni_cyrus_server_init_rc = SASL_OK; +static void pni_cyrus_server_once(void) { + pthread_mutex_lock(&pni_cyrus_mutex); + 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); + } + pni_cyrus_server_started = true; + pni_cyrus_server_init_rc = result; + pthread_mutex_unlock(&pni_cyrus_mutex); +} + +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); +} +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); +} + +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 { + pni_cyrus_client_start(); + result = pni_cyrus_client_init_rc; + if (result!=SASL_OK) break; + + 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, + pnx_sasl_get_remote_fqdn(transport), + NULL, NULL, + callbacks, 0, + &cyrus_conn); + if (result!=SASL_OK) break; + pnx_sasl_set_context(transport, cyrus_conn); + + sasl_security_properties_t secprops = {0}; + secprops.security_flags = + ( 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 = CYRUS_SASL_MAX_BUFFSIZE; + + result = sasl_setprop(cyrus_conn, SASL_SEC_PROPS, &secprops); + if (result!=SASL_OK) break; + + 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 = pnx_sasl_get_external_username(transport); + if (extid) { + result = sasl_setprop(cyrus_conn, SASL_AUTH_EXTERNAL, extid); + } + } while (false); + 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(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*)pnx_sasl_get_context(transport); + do { + + result = sasl_client_start(cyrus_conn, + mechs, + &client_interact, + &out, &outlen, + mechusing); + if (result==SASL_INTERACT) { + pni_cyrus_interact(transport, client_interact); + } + } while (result==SASL_INTERACT); + + pnx_sasl_set_bytes_out(transport, pn_bytes(outlen, out)); + return result; +} + +bool cyrus_sasl_process_mechanisms(pn_transport_t *transport, const char *mechs) +{ + sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport); + const char *mech_selected; + int result = pni_wrap_client_start(transport, mechs, &mech_selected); + switch (result) { + case SASL_OK: + case SASL_CONTINUE: + pnx_sasl_set_selected_mechanism(transport, mech_selected); + pnx_sasl_set_desired_state(transport, SASL_POSTED_INIT); + return true; + case SASL_NOMECH: + default: + pni_check_sasl_result(cyrus_conn, result, transport); + return false; + } +} + + +static int pni_wrap_client_step(pn_transport_t *transport, const pn_bytes_t *in) +{ + sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport); + sasl_interact_t *client_interact=NULL; + const char *out; + unsigned outlen; + + int result; + do { + + result = sasl_client_step(cyrus_conn, + in->start, in->size, + &client_interact, + &out, &outlen); + if (result==SASL_INTERACT) { + pni_cyrus_interact(transport, client_interact); + } + } while (result==SASL_INTERACT); + + pnx_sasl_set_bytes_out(transport, pn_bytes(outlen, out)); + return result; +} + +void cyrus_sasl_process_challenge(pn_transport_t *transport, const pn_bytes_t *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 + 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 + pnx_sasl_fail_authentication(transport); + pnx_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL); + break; + } +} + +void cyrus_sasl_process_outcome(pn_transport_t* transport) +{ +} + +bool cyrus_sasl_init_server(pn_transport_t* transport) +{ + int result; + sasl_conn_t *cyrus_conn = NULL; + do { + 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; + pnx_sasl_set_context(transport, cyrus_conn); + + sasl_security_properties_t secprops = {0}; + secprops.security_flags = + ( 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 = CYRUS_SASL_MAX_BUFFSIZE; + + result = sasl_setprop(cyrus_conn, SASL_SEC_PROPS, &secprops); + if (result!=SASL_OK) break; + + 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 = pnx_sasl_get_external_username(transport); + if (extid) { + result = sasl_setprop(cyrus_conn, SASL_AUTH_EXTERNAL, extid); + } + } while (false); + 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(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*)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 + // with an anonymous init because it is optional. It seems that Cyrus wants an empty string here + // or it will challenge, which the earlier implementation is not prepared for. + // However we can't just always use an empty string as the CRAM-MD5 mech won't allow any data in the server start + if (!in_bytes && strcmp(mech_selected, "ANONYMOUS")==0) { + in_bytes = ""; + in_size = 0; + } else if (in_bytes && strcmp(mech_selected, "CRAM-MD5")==0) { + in_bytes = 0; + in_size = 0; + } + result = sasl_server_start(cyrus_conn, + mech_selected, + in_bytes, in_size, + &out, &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) +{ + sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport); + switch (result) { + case SASL_OK: { + // Authenticated + // Get username from SASL + const void* value; + sasl_getprop(cyrus_conn, SASL_USERNAME, &value); + 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 + pnx_sasl_set_desired_state(transport, SASL_POSTED_CHALLENGE); + break; + default: + pni_check_sasl_result(cyrus_conn, result, transport); + + // Failed to authenticate + pnx_sasl_fail_authentication(transport); + pnx_sasl_set_desired_state(transport, SASL_POSTED_OUTCOME); + break; + } +} + +void cyrus_sasl_process_init(pn_transport_t *transport, const char *mechanism, const pn_bytes_t *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 (!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; + } + } + pni_process_server_result(transport, result); +} + +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*)pnx_sasl_get_context(transport); + result = sasl_server_step(cyrus_conn, + in->start, in->size, + &out, &outlen); + + pnx_sasl_set_bytes_out(transport, pn_bytes(outlen, out)); + return result; +} + +void cyrus_sasl_process_response(pn_transport_t *transport, const pn_bytes_t *recv) +{ + int result = pni_wrap_server_step(transport, recv); + pni_process_server_result(transport, result); +} + +bool cyrus_sasl_can_encrypt(pn_transport_t *transport) +{ + sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport); + if (!cyrus_conn) return false; + + // Get SSF to find out if we need to encrypt or not + const void* value; + int r = sasl_getprop(cyrus_conn, SASL_SSF, &value); + if (r != SASL_OK) { + // TODO: Should log an error here too, maybe assert here + return false; + } + int ssf = *(int *) value; + if (ssf > 0) { + return true; + } + return false; +} + +ssize_t cyrus_sasl_max_encrypt_size(pn_transport_t *transport) +{ + sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport); + if (!cyrus_conn) return PN_ERR; + + const void* value; + int r = sasl_getprop(cyrus_conn, SASL_MAXOUTBUF, &value); + if (r != SASL_OK) { + // TODO: Should log an error here too, maybe assert here + return PN_ERR; + } + int outbuf_size = *(int *) value; + return outbuf_size - + // XXX: this is a clientside workaround/hack to make GSSAPI work as the Cyrus SASL + // 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. + (pnx_sasl_is_client(transport)? 60 : 0); +} + +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*)pnx_sasl_get_context(transport); + const char *output; + unsigned int outlen; + int r = sasl_encode(cyrus_conn, in.start, in.size, &output, &outlen); + if (outlen==0) return 0; + if ( pni_check_io_result(cyrus_conn, r, transport) ) { + *out = pn_bytes(outlen, output); + return outlen; + } + return PN_ERR; +} + +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*)pnx_sasl_get_context(transport); + const char *output; + unsigned int outlen; + int r = sasl_decode(cyrus_conn, in.start, in.size, &output, &outlen); + if (outlen==0) return 0; + if ( pni_check_io_result(cyrus_conn, r, transport) ) { + *out = pn_bytes(outlen, output); + return outlen; + } + return PN_ERR; +} + +void cyrus_sasl_free(pn_transport_t *transport) +{ + sasl_conn_t *cyrus_conn = (sasl_conn_t*)pnx_sasl_get_context(transport); + sasl_dispose(&cyrus_conn); + pnx_sasl_set_context(transport, cyrus_conn); +} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/sasl/cyrus_stub.c ---------------------------------------------------------------------- diff --git a/c/src/sasl/cyrus_stub.c b/c/src/sasl/cyrus_stub.c new file mode 100644 index 0000000..983ace8 --- /dev/null +++ b/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/37136940/c/src/sasl/default_sasl.c ---------------------------------------------------------------------- diff --git a/c/src/sasl/default_sasl.c b/c/src/sasl/default_sasl.c new file mode 100644 index 0000000..66dd318 --- /dev/null +++ b/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 +#include + +// 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/37136940/c/src/sasl/sasl-internal.h ---------------------------------------------------------------------- diff --git a/c/src/sasl/sasl-internal.h b/c/src/sasl/sasl-internal.h new file mode 100644 index 0000000..aade2e0 --- /dev/null +++ b/c/src/sasl/sasl-internal.h @@ -0,0 +1,63 @@ +/* + * + * 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. + * + */ + +#ifndef PROTON_SASL_INTERNAL_H +#define PROTON_SASL_INTERNAL_H 1 + +#include "core/buffer.h" +#include "core/engine-internal.h" + +#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); +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); +void pni_sasl_set_external_security(pn_transport_t *transport, int ssf, const char *authid); + +struct pni_sasl_t { + void *impl_context; + const pnx_sasl_implementation* impl; + char *selected_mechanism; + char *included_mechanisms; + const char *username; + char *password; + const char *remote_fqdn; + char *local_fqdn; + char *external_auth; + int external_ssf; + size_t max_encrypt_size; + pn_buffer_t* decoded_buffer; + pn_buffer_t* encoded_buffer; + pn_bytes_t bytes_out; + pn_sasl_outcome_t outcome; + enum pnx_sasl_state desired_state; + enum pnx_sasl_state last_state; + bool allow_insecure_mechs; + bool client; +}; + +#endif /* sasl-internal.h */ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/sasl/sasl.c ---------------------------------------------------------------------- diff --git a/c/src/sasl/sasl.c b/c/src/sasl/sasl.c new file mode 100644 index 0000000..f015514 --- /dev/null +++ b/c/src/sasl/sasl.c @@ -0,0 +1,918 @@ +/* + * + * 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/autodetect.h" +#include "core/dispatch_actions.h" +#include "core/engine-internal.h" +#include "core/util.h" +#include "protocol.h" + +#include "proton/ssl.h" +#include "proton/types.h" + +#include + +// 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 + return sasl ? ((pn_transport_t *)sasl)->sasl : NULL; +} + +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); +static ssize_t pn_input_read_sasl_encrypt(pn_transport_t *transport, unsigned int layer, const char *bytes, size_t available); +static ssize_t pn_output_write_sasl_header(pn_transport_t* transport, unsigned int layer, char* bytes, size_t size); +static ssize_t pn_output_write_sasl(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available); +static ssize_t pn_output_write_sasl_encrypt(pn_transport_t *transport, unsigned int layer, char *bytes, size_t available); +static void pn_error_sasl(pn_transport_t* transport, unsigned int layer); + +const pn_io_layer_t sasl_header_layer = { + pn_input_read_sasl_header, + pn_output_write_sasl_header, + pn_error_sasl, + NULL, + NULL +}; + +const pn_io_layer_t sasl_write_header_layer = { + pn_input_read_sasl, + pn_output_write_sasl_header, + pn_error_sasl, + NULL, + NULL +}; + +const pn_io_layer_t sasl_read_header_layer = { + pn_input_read_sasl_header, + pn_output_write_sasl, + pn_error_sasl, + NULL, + NULL +}; + +const pn_io_layer_t sasl_layer = { + pn_input_read_sasl, + pn_output_write_sasl, + pn_error_sasl, + NULL, + NULL +}; + +const pn_io_layer_t sasl_encrypt_layer = { + pn_input_read_sasl_encrypt, + pn_output_write_sasl_encrypt, + NULL, + NULL, + NULL +}; + +#define SASL_HEADER ("AMQP\x03\x01\x00\x00") +#define SASL_HEADER_LEN 8 + +static bool pni_sasl_is_server_state(enum pnx_sasl_state state) +{ + return state==SASL_NONE + || state==SASL_POSTED_MECHANISMS + || state==SASL_POSTED_CHALLENGE + || state==SASL_POSTED_OUTCOME + || state==SASL_ERROR; +} + +static bool pni_sasl_is_client_state(enum pnx_sasl_state state) +{ + return state==SASL_NONE + || state==SASL_POSTED_INIT + || state==SASL_POSTED_RESPONSE + || state==SASL_RECVED_OUTCOME_SUCCEED + || state==SASL_RECVED_OUTCOME_FAIL + || state==SASL_ERROR; +} + +static bool pni_sasl_is_final_input_state(pni_sasl_t *sasl) +{ + 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 + || desired_state==SASL_POSTED_OUTCOME; +} + +static bool pni_sasl_is_final_output_state(pni_sasl_t *sasl) +{ + 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 + || last_state==SASL_ERROR + || last_state==SASL_POSTED_OUTCOME; +} + +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); + } +} + +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) { + if (transport->trace & PN_TRACE_DRV) + 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)) { + if (transport->trace & PN_TRACE_DRV) + 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)) { + if (transport->trace & PN_TRACE_DRV) + 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; + } + 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 && changed) pni_emit(transport); + } +} + +// Look for symbol in the mech include list - not particularly 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; + } + } +} + +// Post SASL frame +static void pni_post_sasl_frame(pn_transport_t *transport) +{ + pni_sasl_t *sasl = transport->sasl; + pn_bytes_t out = sasl->bytes_out; + enum pnx_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[szS]", SASL_INIT, sasl->selected_mechanism, + out.size, out.start, sasl->local_fqdn); + pni_emit(transport); + break; + case SASL_POSTED_MECHANISMS: { + // TODO: Hardcoded limit of 16 mechanisms + char *mechs[16]; + char *mechlist = pn_strdup(pni_sasl_impl_list_mechs(transport)); + + int count = 0; + if (mechlist) { + 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: + 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); + } + 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); + 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"); + desired_state = SASL_ERROR; + } + break; + case SASL_RECVED_OUTCOME_SUCCEED: + if (sasl->last_state < SASL_POSTED_INIT) { + desired_state = SASL_POSTED_INIT; + continue; + } + 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"); + desired_state = SASL_ERROR; + break; + case SASL_ERROR: + break; + case SASL_NONE: + return; + } + sasl->last_state = desired_state; + desired_state = sasl->desired_state; + } +} + +static void pn_error_sasl(pn_transport_t* transport, unsigned int layer) +{ + transport->close_sent = true; + 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) +{ + bool eos = pn_transport_capacity(transport)==PN_EOS; + pni_protocol_type_t protocol = pni_sniff_header(bytes, available); + switch (protocol) { + case PNI_PROTOCOL_AMQP_SASL: + if (transport->io_layers[layer] == &sasl_read_header_layer) { + transport->io_layers[layer] = &sasl_layer; + } else { + transport->io_layers[layer] = &sasl_write_header_layer; + } + if (transport->trace & PN_TRACE_FRM) + pn_transport_logf(transport, " <- %s", "SASL"); + pni_sasl_set_external_security(transport, pn_ssl_get_ssf((pn_ssl_t*)transport), pn_ssl_get_remote_subject((pn_ssl_t*)transport)); + return SASL_HEADER_LEN; + case PNI_PROTOCOL_INSUFFICIENT: + if (!eos) return 0; + /* Fallthru */ + default: + break; + } + char quoted[1024]; + pn_quote_data(quoted, 1024, bytes, available); + pn_do_error(transport, "amqp:connection:framing-error", + "%s header mismatch: %s ['%s']%s", "SASL", pni_protocol_name(protocol), quoted, + !eos ? "" : " (connection aborted)"); + pn_set_error_layer(transport); + return PN_EOS; +} + +static void pni_sasl_start_server_if_needed(pn_transport_t *transport) +{ + pni_sasl_t *sasl = transport->sasl; + if (!sasl->client && sasl->desired_statesasl; + + bool eos = pn_transport_capacity(transport)==PN_EOS; + if (eos) { + pn_do_error(transport, "amqp:connection:framing-error", "connection aborted"); + pn_set_error_layer(transport); + return PN_EOS; + } + + pni_sasl_start_server_if_needed(transport); + + if (!pni_sasl_is_final_input_state(sasl)) { + return pn_dispatcher_input(transport, bytes, available, false, &transport->halt); + } + + if (!pni_sasl_is_final_output_state(sasl)) { + return pni_passthru_layer.process_input(transport, layer, bytes, available); + } + + if (pni_sasl_impl_can_encrypt(transport)) { + sasl->max_encrypt_size = pni_sasl_impl_max_encrypt_size(transport); + if (transport->trace & PN_TRACE_DRV) + pn_transport_logf(transport, "SASL Encryption enabled: buffer=%d", sasl->max_encrypt_size); + transport->io_layers[layer] = &sasl_encrypt_layer; + } else { + transport->io_layers[layer] = &pni_passthru_layer; + } + return transport->io_layers[layer]->process_input(transport, layer, bytes, available); +} + +static ssize_t pn_input_read_sasl_encrypt(pn_transport_t* transport, unsigned int layer, const char* bytes, size_t available) +{ + pn_buffer_t *in = transport->sasl->decoded_buffer; + const size_t max_buffer = transport->sasl->max_encrypt_size; + for (size_t processed = 0; processed0) { + size = pn_buffer_append(in, decoded.start, decoded.size); + if (size) return size; + } + processed += decode_size; + } + pn_bytes_t decoded = pn_buffer_bytes(in); + size_t processed_size = 0; + while (processed_size < decoded.size) { + ssize_t size = pni_passthru_layer.process_input(transport, layer, decoded.start+processed_size, decoded.size-processed_size); + if (size==0) break; + if (size<0) return size; + pn_buffer_trim(in, size, 0); + processed_size += size; + } + return available; +} + +static ssize_t pn_output_write_sasl_header(pn_transport_t *transport, unsigned int layer, char *bytes, size_t size) +{ + if (transport->trace & PN_TRACE_FRM) + pn_transport_logf(transport, " -> %s", "SASL"); + assert(size >= SASL_HEADER_LEN); + memmove(bytes, SASL_HEADER, SASL_HEADER_LEN); + if (transport->io_layers[layer]==&sasl_write_header_layer) { + transport->io_layers[layer] = &sasl_layer; + } else { + transport->io_layers[layer] = &sasl_read_header_layer; + } + return SASL_HEADER_LEN; +} + +static ssize_t pn_output_write_sasl(pn_transport_t* transport, unsigned int layer, char* bytes, size_t available) +{ + pni_sasl_t *sasl = transport->sasl; + + // this accounts for when pn_do_error is invoked, e.g. by idle timeout + if (transport->close_sent) return PN_EOS; + + pni_sasl_start_server_if_needed(transport); + + pni_sasl_impl_prepare_write(transport); + + pni_post_sasl_frame(transport); + + if (pn_buffer_size(transport->output_buffer) != 0 || !pni_sasl_is_final_output_state(sasl)) { + return pn_dispatcher_output(transport, bytes, available); + } + + if (!pni_sasl_is_final_input_state(sasl)) { + return pni_passthru_layer.process_output(transport, layer, bytes, available ); + } + + // We only get here if there is nothing to output and we're in a final state + if (sasl->outcome != PN_SASL_OK) { + return PN_EOS; + } + + // We know that auth succeeded or we're not in final input state + if (pni_sasl_impl_can_encrypt(transport)) { + sasl->max_encrypt_size = pni_sasl_impl_max_encrypt_size(transport); + if (transport->trace & PN_TRACE_DRV) + pn_transport_logf(transport, "SASL Encryption enabled: buffer=%d", sasl->max_encrypt_size); + transport->io_layers[layer] = &sasl_encrypt_layer; + } else { + transport->io_layers[layer] = &pni_passthru_layer; + } + return transport->io_layers[layer]->process_output(transport, layer, bytes, available); +} + +static ssize_t pn_output_write_sasl_encrypt(pn_transport_t* transport, unsigned int layer, char* bytes, size_t available) +{ + ssize_t clear_size = pni_passthru_layer.process_output(transport, layer, bytes, available ); + if (clear_size<0) return clear_size; + + const ssize_t max_buffer = transport->sasl->max_encrypt_size; + pn_buffer_t *out = transport->sasl->encoded_buffer; + for (ssize_t processed = 0; processed0) { + size = pn_buffer_append(out, encoded.start, encoded.size); + if (size) return size; + } + processed += encode_size; + } + ssize_t size = pn_buffer_get(out, 0, available, bytes); + pn_buffer_trim(out, size, 0); + return size; +} + +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->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->remote_fqdn = NULL; + sasl->local_fqdn = NULL; + sasl->external_auth = NULL; + sasl->external_ssf = 0; + sasl->outcome = PN_SASL_NONE; + sasl->decoded_buffer = pn_buffer(0); + sasl->encoded_buffer = pn_buffer(0); + sasl->bytes_out.size = 0; + sasl->bytes_out.start = NULL; + sasl->desired_state = SASL_NONE; + sasl->last_state = SASL_NONE; + sasl->allow_insecure_mechs = 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_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->external_auth); + free(sasl->local_fqdn); + + if (sasl->impl_context) { + pni_sasl_impl_free(transport); + } + pn_buffer_free(sasl->decoded_buffer); + pn_buffer_free(sasl->encoded_buffer); + free(sasl); + } + } +} + +void pni_sasl_set_remote_hostname(pn_transport_t * transport, const char * fqdn) +{ + pni_sasl_t *sasl = transport->sasl; + sasl->remote_fqdn = fqdn; +} + +void pnx_sasl_set_local_hostname(pn_transport_t * transport, const char * fqdn) +{ + pni_sasl_t *sasl = transport->sasl; + sasl->local_fqdn = pn_strdup(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 ? pn_strdup(password) : NULL; +} + +void pni_sasl_set_external_security(pn_transport_t *transport, int ssf, const char *authid) +{ + pni_sasl_t *sasl = transport->sasl; + sasl->external_ssf = ssf; + free(sasl->external_auth); + sasl->external_auth = authid ? pn_strdup(authid) : 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 ? pn_strdup(mechs) : NULL; +} + +void pn_sasl_set_allow_insecure_mechs(pn_sasl_t *sasl0, bool insecure) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + sasl->allow_insecure_mechs = insecure; +} + +bool pn_sasl_get_allow_insecure_mechs(pn_sasl_t *sasl0) +{ + pni_sasl_t *sasl = get_sasl_internal(sasl0); + return sasl->allow_insecure_mechs; +} + +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; +} + +// 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_sasl_impl_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; + + // This scanning relies on pn_data_scan leaving the pn_data_t cursors + // where they are after finishing the scan + pn_string_t *mechs = pn_string(""); + + // Try array of symbols for mechanism list + bool array = false; + int err = pn_data_scan(args, "D.[?@[", &array); + if (err) return err; + + if (array) { + // 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 (pnx_sasl_is_included_mech(transport, s)) { + pn_string_addf(mechs, "%*s ", (int)s.size, s.start); + } + } + + if (pn_string_size(mechs)) { + pn_string_buffer(mechs)[pn_string_size(mechs)-1] = 0; + } + } else { + // No array of symbols; try single symbol + pn_data_rewind(args); + pn_bytes_t symbol; + int err = pn_data_scan(args, "D.[s]", &symbol); + if (err) return err; + + pn_string_setn(mechs, symbol.start, symbol.size); + } + + 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; + pnx_sasl_set_desired_state(transport, SASL_RECVED_OUTCOME_FAIL); + } + + 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_sasl_impl_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_sasl_impl_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; + bool authenticated = sasl->outcome==PN_SASL_OK; + transport->authenticated = authenticated; + pnx_sasl_set_desired_state(transport, authenticated ? SASL_RECVED_OUTCOME_SUCCEED : SASL_RECVED_OUTCOME_FAIL); + + pni_sasl_impl_process_outcome(transport); + + return 0; +} + http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/37136940/c/src/security.xml ---------------------------------------------------------------------- diff --git a/c/src/security.xml b/c/src/security.xml new file mode 100644 index 0000000..c6ac018 --- /dev/null +++ b/c/src/security.xml @@ -0,0 +1,76 @@ + + + + + +
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
--------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org For additional commands, e-mail: commits-help@qpid.apache.org