You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ch...@apache.org on 2016/03/30 19:13:40 UTC
[2/3] qpid-dispatch git commit: DISPATCH-188: Implement policy. merge
from branch crolke-DISPATCH-188-1.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/container.c
----------------------------------------------------------------------
diff --git a/src/container.c b/src/container.c
index ddd85ae..11575bc 100644
--- a/src/container.c
+++ b/src/container.c
@@ -21,6 +21,7 @@
#include <string.h>
#include "dispatch_private.h"
#include "connection_manager_private.h"
+#include "policy.h"
#include <qpid/dispatch/container.h>
#include <qpid/dispatch/server.h>
#include <qpid/dispatch/message.h>
@@ -267,6 +268,10 @@ static void notify_opened(qd_container_t *container, qd_connection_t *conn, void
}
}
+void policy_notify_opened(void *container, qd_connection_t *conn, void *context)
+{
+ notify_opened((qd_container_t *)container, (qd_connection_t *)conn, context);
+}
static void notify_closed(qd_container_t *container, qd_connection_t *conn, void *context)
{
@@ -367,11 +372,18 @@ int pn_event_handler(void *handler_context, void *conn_context, pn_event_t *even
switch (pn_event_type(event)) {
case PN_CONNECTION_REMOTE_OPEN :
- if (pn_connection_state(conn) & PN_LOCAL_UNINIT)
- pn_connection_open(conn);
- qd_connection_set_user(qd_conn);
- qd_connection_manager_connection_opened(qd_conn);
- notify_opened(container, qd_conn, conn_context);
+ if (pn_connection_state(conn) & PN_LOCAL_UNINIT) {
+ // This Open is an externally initiated connection
+ // Let policy engine decide
+ qd_connection_set_event_stall(qd_conn, true);
+ qd_conn->open_container = (void *)container;
+ qd_conn->conn_context = conn_context;
+ qd_connection_invoke_deferred(qd_conn, qd_policy_amqp_open, qd_conn);
+ } else {
+ // This Open is in response to an internally initiated connection
+ qd_connection_manager_connection_opened(qd_conn);
+ notify_opened(container, qd_conn, conn_context);
+ }
break;
case PN_CONNECTION_REMOTE_CLOSE :
@@ -383,7 +395,13 @@ int pn_event_handler(void *handler_context, void *conn_context, pn_event_t *even
case PN_SESSION_REMOTE_OPEN :
ssn = pn_event_session(event);
if (pn_session_state(ssn) & PN_LOCAL_UNINIT) {
- pn_session_set_incoming_capacity(ssn, 1000000);
+ if (qd_conn->policy_settings) {
+ if (!qd_policy_approve_amqp_session(ssn, qd_conn)) {
+ break;
+ }
+ qd_conn->n_sessions++;
+ }
+ qd_policy_apply_session_settings(ssn, qd_conn);
pn_session_open(ssn);
}
break;
@@ -400,6 +418,15 @@ int pn_event_handler(void *handler_context, void *conn_context, pn_event_t *even
if (pn_link_session(pn_link) == ssn) {
qd_link_t *qd_link = (qd_link_t *)pn_link_get_context(pn_link);
if (qd_link && qd_link->node) {
+ if (qd_conn->policy_settings) {
+ if (qd_link->direction == QD_OUTGOING) {
+ qd_conn->n_senders--;
+ assert(qd_conn->n_senders >= 0);
+ } else {
+ qd_conn->n_receivers--;
+ assert(qd_conn->n_receivers >= 0);
+ }
+ }
qd_log(container->log_source, QD_LOG_NOTICE,
"Aborting link '%s' due to parent session end",
pn_link_name(pn_link));
@@ -409,6 +436,9 @@ int pn_event_handler(void *handler_context, void *conn_context, pn_event_t *even
}
pn_link = pn_link_next(pn_link, PN_LOCAL_ACTIVE | PN_REMOTE_ACTIVE);
}
+ if (qd_conn->policy_settings) {
+ qd_conn->n_sessions--;
+ }
pn_session_close(ssn);
}
break;
@@ -416,10 +446,23 @@ int pn_event_handler(void *handler_context, void *conn_context, pn_event_t *even
case PN_LINK_REMOTE_OPEN :
pn_link = pn_event_link(event);
if (pn_link_state(pn_link) & PN_LOCAL_UNINIT) {
- if (pn_link_is_sender(pn_link))
+ if (pn_link_is_sender(pn_link)) {
+ if (qd_conn->policy_settings) {
+ if (!qd_policy_approve_amqp_receiver_link(pn_link, qd_conn)) {
+ break;
+ }
+ qd_conn->n_senders++;
+ }
setup_outgoing_link(container, pn_link);
- else
+ } else {
+ if (qd_conn->policy_settings) {
+ if (!qd_policy_approve_amqp_sender_link(pn_link, qd_conn)) {
+ break;
+ }
+ qd_conn->n_receivers++;
+ }
setup_incoming_link(container, pn_link);
+ }
} else if (pn_link_state(pn_link) & PN_LOCAL_ACTIVE)
handle_link_open(container, pn_link);
break;
@@ -433,8 +476,20 @@ int pn_event_handler(void *handler_context, void *conn_context, pn_event_t *even
qd_detach_type_t dt = pn_event_type(event) == PN_LINK_REMOTE_CLOSE ? QD_CLOSED : QD_DETACHED;
if (node)
node->ntype->link_detach_handler(node->context, qd_link, dt);
- else if (qd_link->pn_link == pn_link)
+ else if (qd_link->pn_link == pn_link) {
+ if (qd_conn->policy_settings) {
+ if (pn_link_is_sender(pn_link)) {
+ qd_conn->n_senders--;
+ assert (qd_conn->n_senders >= 0);
+ } else {
+ qd_conn->n_receivers--;
+ assert (qd_conn->n_receivers >= 0);
+ }
+ } else {
+ // no policy - links not counted
+ }
pn_link_close(pn_link);
+ }
if (qd_link->close_sess_with_link && qd_link->pn_sess &&
pn_link_state(pn_link) == (PN_LOCAL_CLOSED | PN_REMOTE_CLOSED))
pn_session_close(qd_link->pn_sess);
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/dispatch.c
----------------------------------------------------------------------
diff --git a/src/dispatch.c b/src/dispatch.c
index c05d24c..73bd0b5 100644
--- a/src/dispatch.c
+++ b/src/dispatch.c
@@ -30,6 +30,7 @@
#include "log_private.h"
#include "router_private.h"
#include "message_private.h"
+#include "policy.h"
#include "entity.h"
#include "entity_cache.h"
#include <dlfcn.h>
@@ -42,6 +43,8 @@ qd_server_t *qd_server(qd_dispatch_t *qd, int tc, const char *container_name,
void qd_server_free(qd_server_t *server);
qd_container_t *qd_container(qd_dispatch_t *qd);
void qd_container_free(qd_container_t *container);
+qd_policy_t *qd_policy(qd_dispatch_t *qd);
+void qd_policy_free(qd_policy_t *policy);
qd_router_t *qd_router(qd_dispatch_t *qd, qd_router_mode_t mode, const char *area, const char *id);
void qd_router_setup_late(qd_dispatch_t *qd);
void qd_router_free(qd_router_t *router);
@@ -67,6 +70,7 @@ qd_dispatch_t *qd_dispatch(const char *python_pkgdir)
qd_message_initialize();
if (qd_error_code()) { qd_dispatch_free(qd); return 0; }
qd->log_source = qd_log_source("DISPATCH");
+ qd->dl_handle = 0;
return qd;
}
@@ -76,15 +80,15 @@ STATIC_ASSERT(sizeof(long) >= sizeof(void*), pointer_is_bigger_than_long);
qd_error_t qd_dispatch_load_config(qd_dispatch_t *qd, const char *config_path)
{
- void *handle = dlopen(QPID_DISPATCH_LIB, RTLD_LAZY | RTLD_NOLOAD);
- if (!handle)
+ qd->dl_handle = dlopen(QPID_DISPATCH_LIB, RTLD_LAZY | RTLD_NOLOAD);
+ if (!qd->dl_handle)
return qd_error(QD_ERROR_RUNTIME, "Cannot locate library %s", QPID_DISPATCH_LIB);
qd_python_lock_state_t lock_state = qd_python_lock();
PyObject *module = PyImport_ImportModule("qpid_dispatch_internal.management.config");
PyObject *configure_dispatch = module ? PyObject_GetAttrString(module, "configure_dispatch") : NULL;
Py_XDECREF(module);
- PyObject *result = configure_dispatch ? PyObject_CallFunction(configure_dispatch, "(lls)", (long)qd, handle, config_path) : NULL;
+ PyObject *result = configure_dispatch ? PyObject_CallFunction(configure_dispatch, "(lls)", (long)qd, qd->dl_handle, config_path) : NULL;
Py_XDECREF(configure_dispatch);
if (!result) qd_error_py();
Py_XDECREF(result);
@@ -155,12 +159,45 @@ qd_error_t qd_dispatch_configure_auto_link(qd_dispatch_t *qd, qd_entity_t *entit
return qd_error_code();
}
+qd_error_t qd_dispatch_configure_policy(qd_dispatch_t *qd, qd_entity_t *entity)
+{
+ qd_error_t err;
+ err = qd_entity_configure_policy(qd->policy, entity);
+ if (err)
+ return err;
+ return QD_ERROR_NONE;
+}
+
+
+qd_error_t qd_dispatch_register_policy_manager(qd_dispatch_t *qd, qd_entity_t *entity)
+{
+ return qd_register_policy_manager(qd->policy, entity);
+}
+
+
+long qd_dispatch_policy_c_counts_alloc()
+{
+ return qd_policy_c_counts_alloc();
+}
+
+
+void qd_dispatch_policy_c_counts_free(long ccounts)
+{
+ qd_policy_c_counts_free(ccounts);
+}
+
+void qd_dispatch_policy_c_counts_refresh(long ccounts, qd_entity_t *entity)
+{
+ qd_policy_c_counts_refresh(ccounts, entity);
+}
+
qd_error_t qd_dispatch_prepare(qd_dispatch_t *qd)
{
qd->server = qd_server(qd, qd->thread_count, qd->container_name, qd->sasl_config_path, qd->sasl_config_name);
qd->container = qd_container(qd);
qd->router = qd_router(qd, qd->router_mode, qd->router_area, qd->router_id);
qd->connection_manager = qd_connection_manager(qd);
+ qd->policy = qd_policy(qd);
return qd_error_code();
}
@@ -177,6 +214,7 @@ void qd_dispatch_free(qd_dispatch_t *qd)
free(qd->container_name);
free(qd->router_area);
qd_connection_manager_free(qd->connection_manager);
+ qd_policy_free(qd->policy);
Py_XDECREF((PyObject*) qd->agent);
qd_router_free(qd->router);
qd_container_free(qd->container);
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/dispatch_private.h
----------------------------------------------------------------------
diff --git a/src/dispatch_private.h b/src/dispatch_private.h
index b955e10..d624f5d 100644
--- a/src/dispatch_private.h
+++ b/src/dispatch_private.h
@@ -40,6 +40,7 @@ typedef struct qd_config_address_t qd_config_address_t;
#include <qpid/dispatch/container.h>
#include <qpid/dispatch/router.h>
#include <qpid/dispatch/connection_manager.h>
+#include "policy.h"
#include "server_private.h"
#include "router_private.h"
@@ -49,6 +50,8 @@ struct qd_dispatch_t {
qd_router_t *router;
void *agent;
qd_connection_manager_t *connection_manager;
+ qd_policy_t *policy;
+ void *dl_handle;
int thread_count;
char *container_name;
@@ -107,6 +110,16 @@ qd_error_t qd_dispatch_configure_lrp(qd_dispatch_t *qd, qd_entity_t *entity);
qd_error_t qd_dispatch_configure_route(qd_dispatch_t *qd, qd_entity_t *entity);
/**
+ * Configure security policy, must be called after qd_dispatch_prepare
+ */
+qd_error_t qd_dispatch_configure_policy(qd_dispatch_t *qd, qd_entity_t *entity);
+
+/**
+ * Configure security policy manager, must be called after qd_dispatch_prepare
+ */
+qd_error_t qd_dispatch_register_policy_manager(qd_dispatch_t *qd, qd_entity_t *entity);
+
+/**
* \brief Configure the logging module from the
* parsed configuration file. This must be called after the
* call to qd_dispatch_prepare completes.
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/policy.c
----------------------------------------------------------------------
diff --git a/src/policy.c b/src/policy.c
new file mode 100644
index 0000000..a0f5295
--- /dev/null
+++ b/src/policy.c
@@ -0,0 +1,758 @@
+/*
+ * 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 <Python.h>
+#include "qpid/dispatch/python_embedded.h"
+#include "policy.h"
+#include "policy_internal.h"
+#include <stdio.h>
+#include <string.h>
+#include "dispatch_private.h"
+#include "connection_manager_private.h"
+#include "qpid/dispatch/container.h"
+#include "qpid/dispatch/server.h"
+#include <proton/message.h>
+#include <proton/condition.h>
+#include <proton/connection.h>
+#include <proton/transport.h>
+#include <proton/error.h>
+#include <proton/event.h>
+
+
+//
+// TODO: when policy dev is more complete lower the log level
+//
+#define POLICY_LOG_LEVEL QD_LOG_CRITICAL
+
+//
+// The current statistics maintained globally through multiple
+// reconfiguration of policy settings.
+//
+static int n_connections = 0;
+static int n_denied = 0;
+static int n_processed = 0;
+
+//
+// error conditions signaled to effect denial
+//
+static char* RESOURCE_LIMIT_EXCEEDED = "amqp:resource-limit-exceeded";
+//static char* UNAUTHORIZED_ACCESS = "amqp:unauthorized-access";
+//static char* CONNECTION_FORCED = "amqp:connection:forced";
+
+//
+// error descriptions signaled to effect denial
+//
+static char* CONNECTION_DISALLOWED = "connection disallowed by local policy";
+static char* SESSION_DISALLOWED = "session disallowed by local policy";
+static char* LINK_DISALLOWED = "link disallowed by local policy";
+
+//
+// Policy configuration/statistics management interface
+//
+struct qd_policy_t {
+ qd_dispatch_t *qd;
+ qd_log_source_t *log_source;
+ void *py_policy_manager;
+ // configured settings
+ int max_connection_limit;
+ char *policyFolder;
+ bool enableAccessRules;
+ // live statistics
+ int connections_processed;
+ int connections_denied;
+ int connections_current;
+};
+
+/** Create the policy structure
+ * @param[in] qd pointer the the qd
+ **/
+qd_policy_t *qd_policy(qd_dispatch_t *qd)
+{
+ qd_policy_t *policy = NEW(qd_policy_t);
+
+ policy->qd = qd;
+ policy->log_source = qd_log_source("POLICY");
+ policy->max_connection_limit = 0;
+ policy->policyFolder = 0;
+ policy->enableAccessRules = false;
+ policy->connections_processed= 0;
+ policy->connections_denied = 0;
+ policy->connections_current = 0;
+
+ qd_log(policy->log_source, QD_LOG_TRACE, "Policy Initialized");
+ return policy;
+}
+
+
+/** Free the policy structure
+ * @param[in] policy pointer to the policy
+ **/
+void qd_policy_free(qd_policy_t *policy)
+{
+ if (policy->policyFolder)
+ free(policy->policyFolder);
+ free(policy);
+}
+
+//
+//
+#define CHECK() if (qd_error_code()) goto error
+
+qd_error_t qd_entity_configure_policy(qd_policy_t *policy, qd_entity_t *entity)
+{
+ policy->max_connection_limit = qd_entity_opt_long(entity, "maximumConnections", 0); CHECK();
+ if (policy->max_connection_limit < 0)
+ return qd_error(QD_ERROR_CONFIG, "maximumConnections must be >= 0");
+ policy->policyFolder =
+ qd_entity_opt_string(entity, "policyFolder", 0); CHECK();
+ policy->enableAccessRules = qd_entity_opt_bool(entity, "enableAccessRules", false); CHECK();
+ qd_log(policy->log_source, QD_LOG_INFO, "Policy configured maximumConnections: %d, policyFolder: '%s', access rules enabled: '%s'",
+ policy->max_connection_limit, policy->policyFolder, (policy->enableAccessRules ? "true" : "false"));
+ return QD_ERROR_NONE;
+
+error:
+ if (policy->policyFolder)
+ free(policy->policyFolder);
+ qd_policy_free(policy);
+ return qd_error_code();
+}
+
+
+//
+//
+qd_error_t qd_register_policy_manager(qd_policy_t *policy, void *policy_manager)
+{
+ policy->py_policy_manager = policy_manager;
+ return QD_ERROR_NONE;
+}
+
+
+long qd_policy_c_counts_alloc()
+{
+ qd_policy_denial_counts_t * dc = NEW(qd_policy_denial_counts_t);
+ assert(dc);
+ memset(dc, 0, sizeof(qd_policy_denial_counts_t));
+ return (long)dc;
+}
+
+
+void qd_policy_c_counts_free(long ccounts)
+{
+ void *dc = (void *)ccounts;
+ assert(dc);
+ free(dc);
+}
+
+
+qd_error_t qd_policy_c_counts_refresh(long ccounts, qd_entity_t *entity)
+{
+ qd_policy_denial_counts_t *dc = (qd_policy_denial_counts_t*)ccounts;
+ if (!qd_entity_set_long(entity, "sessionDenied", dc->sessionDenied) &&
+ !qd_entity_set_long(entity, "senderDenied", dc->senderDenied) &&
+ !qd_entity_set_long(entity, "receiverDenied", dc->receiverDenied) &&
+ !qd_entity_set_long(entity, "dynamicSrcDenied", dc->dynamicSrcDenied) &&
+ !qd_entity_set_long(entity, "anonymousSenderDenied", dc->anonymousSenderDenied) &&
+ !qd_entity_set_long(entity, "linkSourceDenied", dc->linkSourceDenied) &&
+ !qd_entity_set_long(entity, "linkTargetDenied", dc->linkTargetDenied)
+ )
+ return QD_ERROR_NONE;
+ return qd_error_code();
+}
+
+
+/** Update the statistics in qdrouterd.conf["policy"]
+ * @param[in] entity pointer to the policy management object
+ **/
+qd_error_t qd_entity_refresh_policy(qd_entity_t* entity, void *unused) {
+ // Return global stats
+ if (!qd_entity_set_long(entity, "connectionsProcessed", n_processed) &&
+ !qd_entity_set_long(entity, "connectionsDenied", n_denied) &&
+ !qd_entity_set_long(entity, "connectionsCurrent", n_connections)
+ )
+ return QD_ERROR_NONE;
+ return qd_error_code();
+}
+
+
+//
+// Functions related to absolute connection counts.
+// These handle connections at the socket level with
+// no regard to user identity. Simple yes/no decisions
+// are made and there is no AMQP channel for returning
+// error conditions.
+//
+
+bool qd_policy_socket_accept(void *context, const char *hostname)
+{
+ qd_policy_t *policy = (qd_policy_t *)context;
+ bool result = true;
+
+ if (policy->max_connection_limit == 0) {
+ // Policy not in force; connection counted and allowed
+ n_connections += 1;
+ } else {
+ // Policy in force
+ if (n_connections < policy->max_connection_limit) {
+ // connection counted and allowed
+ n_connections += 1;
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Connection '%s' allowed. N= %d", hostname, n_connections);
+ } else {
+ // connection denied
+ result = false;
+ n_denied += 1;
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Connection '%s' denied, N=%d", hostname, n_connections);
+ }
+ }
+ n_processed += 1;
+ return result;
+}
+
+
+//
+//
+void qd_policy_socket_close(void *context, const qd_connection_t *conn)
+{
+ qd_policy_t *policy = (qd_policy_t *)context;
+
+ n_connections -= 1;
+ assert (n_connections >= 0);
+ if (policy->enableAccessRules) {
+ // HACK ALERT: TODO: This should be deferred to a Python thread
+ qd_python_lock_state_t lock_state = qd_python_lock();
+ PyObject *module = PyImport_ImportModule("qpid_dispatch_internal.policy.policy_manager");
+ if (module) {
+ PyObject *close_connection = PyObject_GetAttrString(module, "policy_close_connection");
+ if (close_connection) {
+ PyObject *result = PyObject_CallFunction(close_connection, "(OK)",
+ (PyObject *)policy->py_policy_manager,
+ conn->connection_id);
+ if (result) {
+ Py_XDECREF(result);
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Connection close failed: result");
+ }
+ Py_XDECREF(close_connection);
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Connection close failed: close_connection");
+ }
+ Py_XDECREF(module);
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Connection close failed: module");
+ }
+ qd_python_unlock(lock_state);
+ }
+ if (policy->max_connection_limit > 0) {
+ const char *hostname = qdpn_connector_name(conn->pn_cxtr);
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Connection '%s' closed. N connections=%d", hostname, n_connections);
+ }
+}
+
+
+//
+// Functions related to authenticated connection denial.
+// An AMQP Open has been received over some connection.
+// Evaluate the connection auth and the Open fields to
+// allow or deny the Open. Denied Open attempts are
+// effected by returning Open and then Close_with_condition.
+//
+/** Look up user/host/app in python policyRuleset and give the AMQP Open
+ * a go-no_go decision. Return false if the mechanics of calling python
+ * fails. A policy lookup will deny the connection by returning a blank
+ * usergroup name in the name buffer.
+ * Connection and connection denial counting is done in the python code.
+ * @param[in] policy pointer to policy
+ * @param[in] username authenticated user name
+ * @param[in] hostip numeric host ip address
+ * @param[in] app application name received in remote AMQP Open.hostname
+ * @param[in] conn_name connection name for tracking
+ * @param[out] name_buf pointer to settings name buffer
+ * @param[in] name_buf_size size of settings_buf
+ **/
+bool qd_policy_open_lookup_user(
+ qd_policy_t *policy,
+ const char *username,
+ const char *hostip,
+ const char *app,
+ const char *conn_name,
+ char *name_buf,
+ int name_buf_size,
+ uint64_t conn_id,
+ qd_policy_settings_t *settings)
+{
+ // TODO: crolke 2016-03-24 - Workaround for PROTON-1133: Port number is included in Open hostname
+ // Strip the ':NNNN', if any, from the app name so that policy will work with proton 0.12
+ char appname[HOST_NAME_MAX + 1];
+ strncpy(appname, app, HOST_NAME_MAX);
+ appname[HOST_NAME_MAX] = 0;
+ char * colonp = strstr(appname, ":");
+ if (colonp) {
+ *colonp = 0;
+ }
+ // Lookup the user/host/app for allow/deny and to get settings name
+ bool res = false;
+ qd_python_lock_state_t lock_state = qd_python_lock();
+ PyObject *module = PyImport_ImportModule("qpid_dispatch_internal.policy.policy_manager");
+ if (module) {
+ PyObject *lookup_user = PyObject_GetAttrString(module, "policy_lookup_user");
+ if (lookup_user) {
+ PyObject *result = PyObject_CallFunction(lookup_user, "(OssssK)",
+ (PyObject *)policy->py_policy_manager,
+ username, hostip, appname, conn_name, conn_id);
+ if (result) {
+ const char *res_string = PyString_AsString(result);
+ strncpy(name_buf, res_string, name_buf_size);
+ Py_XDECREF(result);
+ res = true; // settings name returned
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Internal: lookup_user: result");
+ }
+ Py_XDECREF(lookup_user);
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Internal: lookup_user: lookup_user");
+ }
+ }
+ if (!res) {
+ if (module) {
+ Py_XDECREF(module);
+ }
+ qd_python_unlock(lock_state);
+ return false;
+ }
+
+ //
+ if (name_buf[0]) {
+ // Go get the named settings
+ res = false;
+ PyObject *upolicy = PyDict_New();
+ if (upolicy) {
+ PyObject *lookup_settings = PyObject_GetAttrString(module, "policy_lookup_settings");
+ if (lookup_settings) {
+ PyObject *result2 = PyObject_CallFunction(lookup_settings, "(OssO)",
+ (PyObject *)policy->py_policy_manager,
+ appname, name_buf, upolicy);
+ if (result2) {
+ settings->maxFrameSize = qd_entity_opt_long((qd_entity_t*)upolicy, "maxFrameSize", 0);
+ settings->maxMessageSize = qd_entity_opt_long((qd_entity_t*)upolicy, "maxMessageSize", 0);
+ settings->maxSessionWindow = qd_entity_opt_long((qd_entity_t*)upolicy, "maxSessionWindow", 0);
+ settings->maxSessions = qd_entity_opt_long((qd_entity_t*)upolicy, "maxSessions", 0);
+ settings->maxSenders = qd_entity_opt_long((qd_entity_t*)upolicy, "maxSenders", 0);
+ settings->maxReceivers = qd_entity_opt_long((qd_entity_t*)upolicy, "maxReceivers", 0);
+ settings->allowAnonymousSender = qd_entity_opt_bool((qd_entity_t*)upolicy, "allowAnonymousSender", false);
+ settings->allowDynamicSrc = qd_entity_opt_bool((qd_entity_t*)upolicy, "allowDynamicSrc", false);
+ settings->sources = qd_entity_get_string((qd_entity_t*)upolicy, "sources");
+ settings->targets = qd_entity_get_string((qd_entity_t*)upolicy, "targets");
+ settings->denialCounts = (qd_policy_denial_counts_t*)
+ qd_entity_get_long((qd_entity_t*)upolicy, "denialCounts");
+ Py_XDECREF(result2);
+ res = true; // named settings content returned
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Internal: lookup_user: result2");
+ }
+ Py_XDECREF(lookup_settings);
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Internal: lookup_user: lookup_settings");
+ }
+ Py_XDECREF(upolicy);
+ } else {
+ qd_log(policy->log_source, POLICY_LOG_LEVEL, "Internal: lookup_user: upolicy");
+ }
+ }
+ Py_XDECREF(module);
+ qd_python_unlock(lock_state);
+
+ qd_log(policy->log_source,
+ POLICY_LOG_LEVEL,
+ "Policy AMQP Open lookup_user: %s, hostip: %s, app: %s, connection: %s. Usergroup: '%s'%s",
+ username, hostip, appname, conn_name, name_buf, (res ? "" : " Internal error."));
+
+ return res;
+}
+
+
+//
+//
+void qd_policy_private_deny_amqp_connection(pn_connection_t *conn, const char *cond_name, const char *cond_descr)
+{
+ pn_condition_t * cond = pn_connection_condition(conn);
+ (void) pn_condition_set_name( cond, cond_name);
+ (void) pn_condition_set_description(cond, cond_descr);
+ pn_connection_close(conn);
+}
+
+
+//
+//
+void qd_policy_deny_amqp_session(pn_session_t *ssn, qd_connection_t *qd_conn)
+{
+ pn_condition_t * cond = pn_session_condition(ssn);
+ (void) pn_condition_set_name( cond, RESOURCE_LIMIT_EXCEEDED);
+ (void) pn_condition_set_description(cond, SESSION_DISALLOWED);
+ pn_session_close(ssn);
+
+ pn_connection_t *conn = qd_connection_pn(qd_conn);
+ qd_dispatch_t *qd = qd_conn->server->qd;
+ qd_policy_t *policy = qd->policy;
+ pn_transport_t *pn_trans = pn_connection_transport(conn);
+ const char *username = pn_transport_get_user(pn_trans);
+ const char *hostip = qdpn_connector_hostip(qd_conn->pn_cxtr);
+ const char *app = pn_connection_remote_hostname(conn);
+ qd_log(policy->log_source,
+ POLICY_LOG_LEVEL,
+ "Policy AMQP Begin Session denied due to session limit. user: %s, hostip: %s, app: %s",
+ username, hostip, app);
+
+ qd_conn->policy_settings->denialCounts->sessionDenied++;
+}
+
+
+//
+//
+bool qd_policy_approve_amqp_session(pn_session_t *ssn, qd_connection_t *qd_conn)
+{
+ if (qd_conn->policy_settings) {
+ if (qd_conn->policy_settings->maxSessions) {
+ if (qd_conn->n_sessions == qd_conn->policy_settings->maxSessions) {
+ qd_policy_deny_amqp_session(ssn, qd_conn);
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+
+//
+//
+void qd_policy_apply_session_settings(pn_session_t *ssn, qd_connection_t *qd_conn)
+{
+ if (qd_conn->policy_settings && qd_conn->policy_settings->maxSessionWindow) {
+ pn_session_set_incoming_capacity(ssn, qd_conn->policy_settings->maxSessionWindow);
+ } else {
+ pn_session_set_incoming_capacity(ssn, 1000000);
+ }
+}
+
+//
+//
+void _qd_policy_deny_amqp_link(pn_link_t *link, qd_connection_t *qd_conn, char * s_or_r)
+{
+ pn_condition_t * cond = pn_link_condition(link);
+ (void) pn_condition_set_name( cond, RESOURCE_LIMIT_EXCEEDED);
+ (void) pn_condition_set_description(cond, LINK_DISALLOWED);
+ pn_link_close(link);
+
+ pn_connection_t *conn = qd_connection_pn(qd_conn);
+ qd_dispatch_t *qd = qd_conn->server->qd;
+ qd_policy_t *policy = qd->policy;
+ pn_transport_t *pn_trans = pn_connection_transport(conn);
+ const char *username = pn_transport_get_user(pn_trans);
+ const char *hostip = qdpn_connector_hostip(qd_conn->pn_cxtr);
+ const char *app = pn_connection_remote_hostname(conn);
+ qd_log(policy->log_source,
+ POLICY_LOG_LEVEL,
+ "Policy AMQP Attach Link denied due to %s limit. user: %s, hostip: %s, app: %s",
+ s_or_r, username, hostip, app);
+}
+
+
+//
+//
+void _qd_policy_deny_amqp_sender_link(pn_link_t *pn_link, qd_connection_t *qd_conn)
+{
+ _qd_policy_deny_amqp_link(pn_link, qd_conn, "sender");
+ qd_conn->policy_settings->denialCounts->senderDenied++;
+}
+
+
+//
+//
+void _qd_policy_deny_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_conn)
+{
+ _qd_policy_deny_amqp_link(pn_link, qd_conn, "receiver");
+ qd_conn->policy_settings->denialCounts->receiverDenied++;
+}
+
+
+//
+//
+#define MIN(a,b) (((a)<(b))?(a):(b))
+
+char * _qd_policy_link_user_name_subst(const char *uname, const char *proposed, char *obuf, int osize)
+{
+ if (strlen(uname) == 0)
+ return NULL;
+
+ const char *duser = "${user}";
+ char *retptr = obuf;
+ const char *wiptr = proposed;
+ const char *findptr = strstr(proposed, uname);
+ if (findptr == NULL) {
+ return NULL;
+ }
+
+ // Copy leading before match
+ int segsize = findptr - wiptr;
+ int copysize = MIN(osize, segsize);
+ if (copysize)
+ strncpy(obuf, wiptr, copysize);
+ wiptr += copysize;
+ osize -= copysize;
+ obuf += copysize;
+
+ // Copy the substitution string
+ segsize = strlen(duser);
+ copysize = MIN(osize, segsize);
+ if (copysize)
+ strncpy(obuf, duser, copysize);
+ wiptr += strlen(uname);
+ osize -= copysize;
+ obuf += copysize;
+
+ // Copy trailing after match
+ strncpy(obuf, wiptr, osize);
+ return retptr;
+}
+
+
+//
+//
+// Size of 'easy' temporary copy of allowed input string
+#define QPALN_SIZE 1024
+// Size of user-name-substituted proposed string.
+#define QPALN_USERBUFSIZE 300
+// C in the CSV string
+#define QPALN_COMMA_SEP ","
+// Wildcard character
+#define QPALN_WILDCARD '*'
+
+bool _qd_policy_approve_link_name(const char *username, const char *allowed, const char *proposed)
+{
+ // Verify string sizes are usable
+ size_t p_len = strlen(proposed);
+ if (p_len == 0) {
+ // degenerate case of blank name being opened. will never match anything.
+ return false;
+ }
+ size_t a_len = strlen(allowed);
+ if (a_len == 0) {
+ // no names in 'allowed'.
+ return false;
+ }
+
+ // Create a temporary writable copy of incoming allowed list
+ char t_allow[QPALN_SIZE + 1]; // temporary buffer for normal allow lists
+ char * pa = t_allow;
+ if (a_len > QPALN_SIZE) {
+ pa = (char *)malloc(a_len + 1); // malloc a buffer for larger allow lists
+ }
+ strncpy(pa, allowed, a_len);
+ pa[a_len] = 0;
+ // Do reverse user substitution into proposed
+ char substbuf[QPALN_USERBUFSIZE];
+ char * prop2 = _qd_policy_link_user_name_subst(username, proposed, substbuf, QPALN_USERBUFSIZE);
+ char *tok, *toknext;
+ tok = strtok_r(pa, QPALN_COMMA_SEP, &toknext);
+ assert (tok);
+ bool result = false;
+ while (tok != NULL) {
+ if (*tok == QPALN_WILDCARD) {
+ result = true;
+ break;
+ }
+ int matchlen = p_len;
+ int len = strlen(tok);
+ if (tok[len-1] == QPALN_WILDCARD) {
+ matchlen = len - 1;
+ assert(len > 0);
+ }
+ if (strncmp(tok, proposed, matchlen) == 0) {
+ result = true;
+ break;
+ }
+ if (prop2 && strncmp(tok, prop2, matchlen) == 0) {
+ result = true;
+ break;
+ }
+ tok = strtok_r(NULL, QPALN_COMMA_SEP, &toknext);
+ }
+ if (pa != t_allow) {
+ free(pa);
+ }
+ return result;
+}
+
+//
+//
+bool qd_policy_approve_amqp_sender_link(pn_link_t *pn_link, qd_connection_t *qd_conn)
+{
+ pn_connection_t *conn = qd_connection_pn(qd_conn);
+ pn_transport_t *pn_trans = pn_connection_transport(conn);
+ const char *username = pn_transport_get_user(pn_trans);
+ if (qd_conn->policy_settings->maxSenders) {
+ if (qd_conn->n_senders == qd_conn->policy_settings->maxSenders) {
+ // Max sender limit specified and violated.
+ _qd_policy_deny_amqp_sender_link(pn_link, qd_conn);
+ return false;
+ } else {
+ // max sender limit not violated
+ }
+ } else {
+ // max sender limit not specified
+ }
+ // Deny sender link based on target
+ const char * target = pn_terminus_get_address(pn_link_remote_target(pn_link));
+ bool lookup;
+ if (target && *target) {
+ // a target is specified
+ lookup = _qd_policy_approve_link_name(username, qd_conn->policy_settings->targets, target);
+
+ qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_TRACE,
+ "Approve sender link '%s' for user '%s': %s",
+ target, username, (lookup ? "ALLOW" : "DENY"));
+
+ if (!lookup) {
+ _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn);
+ return false;
+ }
+ } else {
+ // A sender with no remote target.
+ // This happens all the time with anonymous relay
+ lookup = qd_conn->policy_settings->allowAnonymousSender;
+ qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_TRACE,
+ "Approve anonymous sender for user '%s': %s",
+ username, (lookup ? "ALLOW" : "DENY"));
+ if (!lookup) {
+ _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+bool qd_policy_approve_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_conn)
+{
+ pn_connection_t *conn = qd_connection_pn(qd_conn);
+ pn_transport_t *pn_trans = pn_connection_transport(conn);
+ const char *username = pn_transport_get_user(pn_trans);
+ if (qd_conn->policy_settings->maxReceivers) {
+ if (qd_conn->n_receivers == qd_conn->policy_settings->maxReceivers) {
+ // Max sender limit specified and violated.
+ _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn);
+ return false;
+ } else {
+ // max receiver limit not violated
+ }
+ } else {
+ // max receiver limit not specified
+ }
+ // Deny receiver link based on source
+ bool dynamic_src = pn_terminus_is_dynamic(pn_link_remote_source(pn_link));
+ if (dynamic_src) {
+ bool lookup = qd_conn->policy_settings->allowDynamicSrc;
+ qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_TRACE,
+ "Approve dynamic source for user '%s': %s",
+ username, (lookup ? "ALLOW" : "DENY"));
+ // Dynamic source policy rendered the decision
+ if (!lookup) {
+ _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn);
+ }
+ return lookup;
+ }
+ const char * source = pn_terminus_get_address(pn_link_remote_source(pn_link));
+ if (source && *source) {
+ // a source is specified
+ bool lookup = _qd_policy_approve_link_name(username, qd_conn->policy_settings->sources, source);
+
+ qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_TRACE,
+ "Approve receiver link '%s' for user '%s': %s",
+ source, username, (lookup ? "ALLOW" : "DENY"));
+
+ if (!lookup) {
+ _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn);
+ return false;
+ }
+ } else {
+ // A receiver with no remote source.
+ qd_log(qd_conn->server->qd->policy->log_source, QD_LOG_TRACE,
+ "Approve receiver link '' for user '%s': DENY",
+ username);
+
+ _qd_policy_deny_amqp_receiver_link(pn_link, qd_conn);
+ return false;
+ }
+ return true;
+}
+
+
+//
+//
+void qd_policy_amqp_open(void *context, bool discard)
+{
+ qd_connection_t *qd_conn = (qd_connection_t *)context;
+ if (!discard) {
+ pn_connection_t *conn = qd_connection_pn(qd_conn);
+ qd_dispatch_t *qd = qd_conn->server->qd;
+ qd_policy_t *policy = qd->policy;
+ bool connection_allowed = true;
+
+ if (policy->enableAccessRules) {
+ // Open connection or not based on policy.
+ // username = pn_connection_get_user(conn) returns blank when
+ // the transport returns 'anonymous'.
+ pn_transport_t *pn_trans = pn_connection_transport(conn);
+ const char *username = pn_transport_get_user(pn_trans);
+
+ const char *hostip = qdpn_connector_hostip(qd_conn->pn_cxtr);
+ const char *app = pn_connection_remote_hostname(conn);
+ const char *conn_name = qdpn_connector_name(qd_conn->pn_cxtr);
+#define SETTINGS_NAME_SIZE 256
+ char settings_name[SETTINGS_NAME_SIZE];
+ uint32_t conn_id = qd_conn->connection_id;
+ qd_conn->policy_settings = NEW(qd_policy_settings_t); // TODO: memory pool for settings
+ memset(qd_conn->policy_settings, 0, sizeof(qd_policy_settings_t));
+
+ if (qd_policy_open_lookup_user(policy, username, hostip, app, conn_name,
+ settings_name, SETTINGS_NAME_SIZE, conn_id,
+ qd_conn->policy_settings) &&
+ settings_name[0]) {
+ // This connection is allowed by policy.
+ // Apply tranport policy settings
+ if (qd_conn->policy_settings->maxFrameSize > 0)
+ pn_transport_set_max_frame(pn_trans, qd_conn->policy_settings->maxFrameSize);
+ if (qd_conn->policy_settings->maxSessions > 0)
+ pn_transport_set_channel_max(pn_trans, qd_conn->policy_settings->maxSessions);
+ } else {
+ // This connection is denied by policy.
+ connection_allowed = false;
+ qd_policy_private_deny_amqp_connection(conn, RESOURCE_LIMIT_EXCEEDED, CONNECTION_DISALLOWED);
+ }
+ } else {
+ // This connection not subject to policy and implicitly allowed.
+ // Note that connections not goverened by policy have no policy_settings.
+ }
+ if (connection_allowed) {
+ if (pn_connection_state(conn) & PN_LOCAL_UNINIT)
+ pn_connection_open(conn);
+ qd_connection_manager_connection_opened(qd_conn);
+ policy_notify_opened(qd_conn->open_container, qd_conn, qd_conn->conn_context);
+ }
+ }
+ qd_connection_set_event_stall(qd_conn, false);
+}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/policy.h
----------------------------------------------------------------------
diff --git a/src/policy.h b/src/policy.h
new file mode 100644
index 0000000..d4fc2b2
--- /dev/null
+++ b/src/policy.h
@@ -0,0 +1,165 @@
+#ifndef __policy_h__
+#define __policy_h__
+/*
+ * 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 "qpid/dispatch.h"
+#include "qpid/dispatch/server.h"
+#include "qpid/dispatch/ctools.h"
+#include "qpid/dispatch/static_assert.h"
+
+#include "config.h"
+#include "alloc.h"
+#include "entity.h"
+#include "entity_cache.h"
+#include <dlfcn.h>
+
+typedef struct qd_policy_denial_counts_s qd_policy_denial_counts_t;
+
+// TODO: Provide locking
+struct qd_policy_denial_counts_s {
+ int sessionDenied;
+ int senderDenied;
+ int receiverDenied;
+ int dynamicSrcDenied;
+ int anonymousSenderDenied;
+ int linkSourceDenied;
+ int linkTargetDenied;
+};
+
+typedef struct qd_policy_t qd_policy_t;
+
+struct qd_policy__settings_s {
+ int maxFrameSize;
+ int maxMessageSize;
+ int maxSessionWindow;
+ int maxSessions;
+ int maxSenders;
+ int maxReceivers;
+ bool allowDynamicSrc;
+ bool allowAnonymousSender;
+ char *sources;
+ char *targets;
+ qd_policy_denial_counts_t *denialCounts;
+};
+
+typedef struct qd_policy__settings_s qd_policy_settings_t;
+
+/** Configure the C policy entity from the settings in qdrouterd.conf["policy"]
+ * Called python-to-C during config processing.
+ * @param[in] policy pointer to the policy
+ * @param[in] entity pointer to the managed entity
+ * @return error or not. If error then the policy is freed.
+ **/
+qd_error_t qd_entity_configure_policy(qd_policy_t *policy, qd_entity_t *entity);
+
+/** Memorize the address of python policy_manager object.
+ * This python object gets called by C to execute user lookups
+ * @param[in] policy pointer to the policy
+ * @param[in] policy_manager the address of the policy_manager object
+ **/
+qd_error_t qd_register_policy_manager(qd_policy_t *policy, void *policy_manager);
+
+
+/** Allocate counts statistics block.
+ * Called from Python
+ */
+long qd_policy_c_counts_alloc();
+
+/** Free counts statistics block.
+ * Called from Python
+ */
+void qd_policy_c_counts_free(long ccounts);
+
+/** Refresh a counts statistics block
+ * Called from Python
+ */
+qd_error_t qd_policy_c_counts_refresh(long ccounts, qd_entity_t*entity);
+
+
+/** Allow or deny an incoming connection based on connection count(s).
+ * A server listener has just accepted a socket.
+ * Allow or deny this connection based on the absolute number
+ * of allowed connections.
+ * The identity of the connecting user has not been negotiated yet.
+ * @param[in] context the current policy
+ * @param[in] name the connector name
+ * @return the connection is allowed or not
+ **/
+bool qd_policy_socket_accept(void *context, const char *hostname);
+
+
+/** Record a closing connection.
+ * A server listener is closing a socket.
+ * Release the counted connection against provisioned limits
+ *
+ * @param[in] context the current policy
+ * @param[in] conn qd_connection
+ **/
+void qd_policy_socket_close(void *context, const qd_connection_t *conn);
+
+
+/** Approve a new session based on connection's policy.
+ * Sessions denied are closed and counted.
+ *
+ * @param[in] ssn proton session being approved
+ * @param[in] qd_conn dispatch connection with policy settings and counts
+ **/
+bool qd_policy_approve_amqp_session(pn_session_t *ssn, qd_connection_t *qd_conn);
+
+
+/** Apply policy or default settings for a new session.
+ *
+ * @param[in] ssn proton session being set
+ * @param[in] qd_conn dispatch connection with policy settings and counts
+ **/
+void qd_policy_apply_session_settings(pn_session_t *ssn, qd_connection_t *qd_conn);
+
+
+/** Approve a new sender link based on connection's policy.
+ * Links denied are closed and counted.
+ *
+ * @param[in] pn_link proton link being approved
+ * @param[in] qd_conn dispatch connection with policy settings and counts
+ **/
+bool qd_policy_approve_amqp_sender_link(pn_link_t *pn_link, qd_connection_t *qd_conn);
+
+
+/** Approve a new receiver link based on connection's policy.
+ * Links denied are closed and counted.
+ *
+ * @param[in] pn_link proton link being approved
+ * @param[in] qd_conn dispatch connection with policy settings and counts
+ **/
+bool qd_policy_approve_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_conn);
+
+
+/** Allow or deny an incoming connection.
+ * An Open performative was received over a new connection.
+ * Consult local policy to determine if this host/user is
+ * allowed to make this connection.
+ * Denied pn_connections are closed with a condition.
+ * Allowed connections are signaled through qd_connection_manager.
+ * This function is called from the deferred queue.
+ * @param[in] context a qd_connection_t object
+ * @param[in] discard callback switch
+ **/
+void qd_policy_amqp_open(void *context, bool discard);
+
+#endif
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/policy_internal.h
----------------------------------------------------------------------
diff --git a/src/policy_internal.h b/src/policy_internal.h
new file mode 100644
index 0000000..9e865e6
--- /dev/null
+++ b/src/policy_internal.h
@@ -0,0 +1,103 @@
+#ifndef __policy_internal_h__
+#define __policy_internal_h__
+/*
+ * 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 "policy.h"
+
+/**
+ * Private Function Prototypes
+ */
+/** Set the error condition and close the connection.
+ * Over the wire this will send an open frame followed
+ * immediately by a close frame with the error condition.
+ * @param[in] conn proton connection being closed
+ * @param[in] cond_name condition name
+ * @param[in] cond_descr condition description
+ **/
+void qd_policy_private_deny_amqp_connection(pn_connection_t *conn, const char *cond_name, const char *cond_descr);
+
+
+/** Internal function to deny an amqp session
+ * The session is closed with a condition and the denial is logged and counted.
+ * @param[in,out] ssn proton session being closed
+ * @param[in,out] qd_conn dispatch connection
+ */
+void qd_policy_deny_amqp_session(pn_session_t *ssn, qd_connection_t *qd_conn);
+
+
+/** Internal function to deny an amqp link
+ * The link is closed and the denial is logged but not counted.
+ * @param[in] link proton link being closed
+ * @param[in] qd_conn the qd conection
+ * @param[in] s_or_r 'sender' or 'receiver' for logging
+ */
+void _qd_policy_deny_amqp_link(pn_link_t *link, qd_connection_t *qd_conn, char * s_or_r);
+
+
+/** Internal function to deny a sender amqp link
+ * The link is closed and the denial is logged but not counted.
+ * @param[in] link proton link to close
+ * @param[in] qd_conn the qd conection
+ * @param[in] s_or_r 'sender' or 'receiver' for logging
+ */
+void _qd_policy_deny_amqp_sender_link(pn_link_t *pn_link, qd_connection_t *qd_conn);
+
+
+/** Internal function to deny a receiver amqp link
+ * The link is closed and the denial is logged but not counted.
+ * @param[in] link proton link to close
+ * @param[in] qd_conn the qd conection
+ * @param[in] s_or_r 'sender' or 'receiver' for logging
+ */
+void _qd_policy_deny_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_conn);
+
+
+/** Perform user name substitution into proposed link name.
+ * The scheme is to substitute '${user}' into the incoming link name whereever the
+ * the username is present. Then it can be matched against the original template with
+ * a minimum of substitutions. For example:
+ * uname : joe
+ * proposed : temp_joe_1
+ * obuf : temp_${user}_1
+ * Note: substituted names are limited to osize characters
+ * Note: only the first (leftmost) user name is substituted.
+ *
+ * @param[in] uname auth user name
+ * @param[in] proposed the link name from the AMQP frame
+ * @param[out] obuf where the constructed link name is returned
+ * @param[in] osize size in bytes of obuf
+ * @return NULL if uname is not present in proposed link name.
+ */
+char * _qd_policy_link_user_name_subst(const char *uname, const char *proposed, char *obuf, int osize);
+
+
+/** Approve link by source/target name.
+ * This match supports trailing wildcard match:
+ * proposed 'temp-305' matches allowed 'temp-*'
+ * This match supports username substitution:
+ * user 'joe', proposed 'temp-joe' matches allowed 'temp-${user}'
+ * Both username substitution and wildcards are allowed:
+ * user 'joe', proposed 'temp-joe-100' matches allowed 'temp-${user}*'
+ * @param[in] username authenticated user name
+ * @param[in] allowed policy settings source/target string in packed CSV form.
+ * @param[in] proposed the link target name to be approved
+ */
+bool _qd_policy_approve_link_name(const char *username, const char *allowed, const char *proposed);
+#endif
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/posix/driver.c
----------------------------------------------------------------------
diff --git a/src/posix/driver.c b/src/posix/driver.c
index 58c5e0c..98e1be4 100644
--- a/src/posix/driver.c
+++ b/src/posix/driver.c
@@ -104,6 +104,7 @@ struct qdpn_connector_t {
DEQ_LINKS(qdpn_connector_t);
qdpn_driver_t *driver;
char name[PN_NAME_MAX];
+ char hostip[PN_NAME_MAX];
pn_timestamp_t wakeup;
pn_connection_t *connection;
pn_transport_t *transport;
@@ -373,12 +374,13 @@ void qdpn_listener_set_context(qdpn_listener_t *listener, void *context)
listener->context = context;
}
-qdpn_connector_t *qdpn_listener_accept(qdpn_listener_t *l)
+qdpn_connector_t *qdpn_listener_accept(qdpn_listener_t *l, void *policy, bool (*policy_fn)(void *, const char *name))
{
if (!l || !l->pending) return NULL;
char name[PN_NAME_MAX];
char host[MAX_HOST];
char serv[MAX_SERV];
+ char hostip[MAX_HOST];
struct sockaddr_in addr = {0};
addr.sin_family = AF_UNSPEC;
@@ -390,7 +392,8 @@ qdpn_connector_t *qdpn_listener_accept(qdpn_listener_t *l)
return 0;
} else {
int code;
- if ((code = getnameinfo((struct sockaddr *) &addr, addrlen, host, MAX_HOST, serv, MAX_SERV, 0))) {
+ if ((code = getnameinfo((struct sockaddr *) &addr, addrlen, host, MAX_HOST, serv, MAX_SERV, 0)) ||
+ (code = getnameinfo((struct sockaddr *) &addr, addrlen, hostip, MAX_HOST, 0, 0, NI_NUMERICHOST))) {
qd_log(l->driver->log, QD_LOG_ERROR, "getnameinfo: %s\n", gai_strerror(code));
close(sock);
return 0;
@@ -400,11 +403,17 @@ qdpn_connector_t *qdpn_listener_accept(qdpn_listener_t *l)
}
}
+ if (policy_fn && !(*policy_fn)(policy, name)) {
+ close(sock);
+ return 0;
+ }
+
if (l->driver->trace & (PN_TRACE_FRM | PN_TRACE_RAW | PN_TRACE_DRV))
fprintf(stderr, "Accepted from %s\n", name);
qdpn_connector_t *c = qdpn_connector_fd(l->driver, sock, NULL);
snprintf(c->name, PN_NAME_MAX, "%s", name);
+ snprintf(c->hostip, PN_NAME_MAX, "%s", hostip);
c->listener = l;
return c;
}
@@ -608,6 +617,12 @@ const char *qdpn_connector_name(const qdpn_connector_t *ctor)
return ctor->name;
}
+const char *qdpn_connector_hostip(const qdpn_connector_t *ctor)
+{
+ if (!ctor) return 0;
+ return ctor->hostip;
+}
+
qdpn_listener_t *qdpn_connector_listener(qdpn_connector_t *ctor)
{
return ctor ? ctor->listener : NULL;
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/server.c
----------------------------------------------------------------------
diff --git a/src/server.c b/src/server.c
index 173c175..5248540 100644
--- a/src/server.c
+++ b/src/server.c
@@ -25,6 +25,7 @@
#include "entity.h"
#include "entity_cache.h"
#include "dispatch_private.h"
+#include "policy.h"
#include "server_private.h"
#include "timer_private.h"
#include "alloc.h"
@@ -75,6 +76,19 @@ static qd_thread_t *thread(qd_server_t *qd_server, int id)
return thread;
}
+static void free_qd_connection(qd_connection_t *ctx)
+{
+ if (ctx->policy_settings) {
+ if (ctx->policy_settings->sources)
+ free(ctx->policy_settings->sources);
+ if (ctx->policy_settings->targets)
+ free(ctx->policy_settings->targets);
+ free (ctx->policy_settings);
+ ctx->policy_settings = 0;
+ }
+ free_qd_connection_t(ctx);
+}
+
qd_error_t qd_entity_update_connection(qd_entity_t* entity, void *impl);
/**
@@ -455,7 +469,7 @@ static void thread_process_listeners_LH(qd_server_t *qd_server)
qd_connection_t *ctx;
for (listener = qdpn_driver_listener(driver); listener; listener = qdpn_driver_listener(driver)) {
- cxtr = qdpn_listener_accept(listener);
+ cxtr = qdpn_listener_accept(listener, qd_server->qd->policy, &qd_policy_socket_accept);
if (!cxtr)
continue;
@@ -463,6 +477,7 @@ static void thread_process_listeners_LH(qd_server_t *qd_server)
qd_log(qd_server->log_source, QD_LOG_DEBUG, "Accepting %s",
log_incoming(logbuf, sizeof(logbuf), cxtr));
+
ctx = new_qd_connection_t();
DEQ_ITEM_INIT(ctx);
ctx->server = qd_server;
@@ -480,8 +495,14 @@ static void thread_process_listeners_LH(qd_server_t *qd_server)
ctx->link_context = 0;
ctx->ufd = 0;
ctx->connection_id = qd_server->next_connection_id++; // Increment the connection id so the next connection can use it
+ ctx->policy_settings = 0;
+ ctx->n_senders = 0;
+ ctx->n_receivers = 0;
+ ctx->open_container = 0;
+ ctx->conn_context = 0;
DEQ_INIT(ctx->deferred_calls);
ctx->deferred_call_lock = sys_mutex();
+ ctx->event_stall = false;
pn_connection_t *conn = pn_connection();
ctx->collector = pn_collector();
@@ -652,40 +673,41 @@ static int process_connector(qd_server_t *qd_server, qdpn_connector_t *cxtr)
pn_event_t *event;
events = 0;
- event = pn_collector_peek(collector);
- while (event) {
- //
- // If we are transitioning to the open state, notify the client via callback.
- //
- if (!ctx->opened && pn_event_type(event) == PN_CONNECTION_REMOTE_OPEN) {
- ctx->opened = true;
- qd_conn_event_t ce = QD_CONN_EVENT_LISTENER_OPEN;
-
- if (ctx->connector) {
- ce = QD_CONN_EVENT_CONNECTOR_OPEN;
- ctx->connector->delay = 0;
- } else
- assert(ctx->listener);
-
- qd_server->conn_handler(qd_server->conn_handler_context,
- ctx->context, ce, (qd_connection_t*) qdpn_connector_context(cxtr));
- events = 1;
- break; // Break without popping this event. It will be re-processed in OPERATIONAL state.
- } else if (pn_event_type(event) == PN_TRANSPORT_ERROR) {
- ctx->closed = true;
- qdpn_connector_close(cxtr);
- if (ctx->connector) {
- const qd_server_config_t *config = ctx->connector->config;
- qd_log(qd_server->log_source, QD_LOG_TRACE, "Connection to %s:%s failed", config->host, config->port);
+ if (!ctx->event_stall) {
+ event = pn_collector_peek(collector);
+ while (event) {
+ //
+ // If we are transitioning to the open state, notify the client via callback.
+ //
+ if (!ctx->opened && pn_event_type(event) == PN_CONNECTION_REMOTE_OPEN) {
+ ctx->opened = true;
+ qd_conn_event_t ce = QD_CONN_EVENT_LISTENER_OPEN;
+
+ if (ctx->connector) {
+ ce = QD_CONN_EVENT_CONNECTOR_OPEN;
+ ctx->connector->delay = 0;
+ } else
+ assert(ctx->listener);
+
+ qd_server->conn_handler(qd_server->conn_handler_context,
+ ctx->context, ce, (qd_connection_t*) qdpn_connector_context(cxtr));
+ events = 1;
+ } else if (pn_event_type(event) == PN_TRANSPORT_ERROR) {
+ ctx->closed = true;
+ qdpn_connector_close(cxtr);
+ if (ctx->connector) {
+ const qd_server_config_t *config = ctx->connector->config;
+ qd_log(qd_server->log_source, QD_LOG_TRACE, "Connection to %s:%s failed", config->host, config->port);
+ }
}
- }
- events += qd_server->pn_event_handler(qd_server->conn_handler_context, ctx->context, event, qd_conn);
- pn_collector_pop(collector);
- event = pn_collector_peek(collector);
- }
+ events += qd_server->pn_event_handler(qd_server->conn_handler_context, ctx->context, event, qd_conn);
+ pn_collector_pop(collector);
- events += qd_server->conn_handler(qd_server->conn_handler_context, ctx->context, QD_CONN_EVENT_WRITABLE, qd_conn);
+ event = ctx->event_stall ? 0 : pn_collector_peek(collector);
+ }
+ events += qd_server->conn_handler(qd_server->conn_handler_context, ctx->context, QD_CONN_EVENT_WRITABLE, qd_conn);
+ }
} while (events > 0);
return passes > 1;
@@ -929,6 +951,10 @@ static void *thread_run(void *arg)
sys_mutex_lock(qd_server->lock);
DEQ_REMOVE(qd_server->connections, ctx);
+ if (!ctx->connector) {
+ qd_policy_socket_close(qd_server->qd->policy, ctx);
+ }
+
qdpn_connector_free(cxtr);
if (conn) {
pn_connection_set_context(conn, 0);
@@ -938,7 +964,7 @@ static void *thread_run(void *arg)
pn_collector_free(ctx->collector);
invoke_deferred_calls(ctx, true); // Discard any pending deferred calls
sys_mutex_free(ctx->deferred_call_lock);
- free_qd_connection_t(ctx);
+ free_qd_connection(ctx);
qd_server->threads_active--;
sys_mutex_unlock(qd_server->lock);
} else {
@@ -1027,9 +1053,15 @@ static void cxtr_try_open(void *context)
ctx->user_context = 0;
ctx->link_context = 0;
ctx->ufd = 0;
-
+ ctx->policy_settings = 0;
+ ctx->n_senders = 0;
+ ctx->n_receivers = 0;
+ ctx->open_container = 0;
+ ctx->conn_context = 0;
+
DEQ_INIT(ctx->deferred_calls);
ctx->deferred_call_lock = sys_mutex();
+ ctx->event_stall = false;
qd_log(ct->server->log_source, QD_LOG_TRACE, "Connecting to %s:%s", ct->config->host, ct->config->port);
@@ -1053,7 +1085,7 @@ static void cxtr_try_open(void *context)
if (ctx->pn_cxtr == 0) {
sys_mutex_free(ctx->deferred_call_lock);
- free_qd_connection_t(ctx);
+ free_qd_connection(ctx);
ct->delay = 10000;
qd_timer_schedule(ct->timer, ct->delay);
return;
@@ -1489,6 +1521,14 @@ void qd_connection_invoke_deferred(qd_connection_t *conn, qd_deferred_t call, vo
}
+void qd_connection_set_event_stall(qd_connection_t *conn, bool stall)
+{
+ conn->event_stall = stall;
+ if (!stall)
+ qd_server_activate(conn);
+}
+
+
qd_listener_t *qd_server_listen(qd_dispatch_t *qd, const qd_server_config_t *config, void *context)
{
qd_server_t *qd_server = qd->server;
@@ -1591,8 +1631,14 @@ qd_user_fd_t *qd_user_fd(qd_dispatch_t *qd, int fd, void *context)
ctx->user_context = 0;
ctx->link_context = 0;
ctx->ufd = ufd;
+ ctx->policy_settings = 0;
+ ctx->n_senders = 0;
+ ctx->n_receivers = 0;
+ ctx->open_container = 0;
+ ctx->conn_context = 0;
DEQ_INIT(ctx->deferred_calls);
ctx->deferred_call_lock = sys_mutex();
+ ctx->event_stall = false;
ufd->context = context;
ufd->server = qd_server;
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/src/server_private.h
----------------------------------------------------------------------
diff --git a/src/server_private.h b/src/server_private.h
index 7a0d1db..11da8e2 100644
--- a/src/server_private.h
+++ b/src/server_private.h
@@ -100,8 +100,15 @@ struct qd_connection_t {
qd_user_fd_t *ufd;
uint64_t connection_id; // A unique identifier for the qd_connection_t. The underlying pn_connection already has one but it is long and clunky.
const char *user_id; // A unique identifier for the user on the connection. This is currently populated from the client ssl cert. See ssl_uid_format in server.h for more info
+ qd_policy_settings_t *policy_settings;
+ int n_sessions;
+ int n_senders;
+ int n_receivers;
+ void *open_container;
+ void *conn_context;
qd_deferred_call_list_t deferred_calls;
sys_mutex_t *deferred_call_lock;
+ bool event_stall;
};
DEQ_DECLARE(qd_connection_t, qd_connection_list_t);
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index f4f35d9..cd79939 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -28,6 +28,7 @@ include_directories(
set(unit_test_SOURCES
compose_test.c
parse_test.c
+ policy_test.c
run_unit_tests.c
server_test.c
timer_test.c
@@ -65,6 +66,7 @@ add_test(unit_tests ${TEST_WRAP} --vg unit_tests ${CMAKE_CURRENT_SOUR
# Unit test python modules
add_test(router_engine_test ${TEST_WRAP} -m unittest -v router_engine_test)
add_test(managemet_test ${TEST_WRAP} -m unittest -v management)
+add_test(router_policy_test ${TEST_WRAP} -m unittest -v router_policy_test)
# System test python modules
foreach(py_test_module
@@ -72,6 +74,7 @@ foreach(py_test_module
system_tests_link_routes
system_tests_management
system_tests_one_router
+ system_tests_policy
system_tests_protocol_family
system_tests_qdmanage
system_tests_qdstat
@@ -93,9 +96,17 @@ list(APPEND SYSTEM_TEST_FILES
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-2/A-ssl.conf.in ${CMAKE_CURRENT_BINARY_DIR}/config-2/A-ssl.conf)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-2/B-ssl.conf.in ${CMAKE_CURRENT_BINARY_DIR}/config-2/B-ssl.conf)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/system_tests_policy.py.in ${CMAKE_CURRENT_BINARY_DIR}/system_tests_policy.py)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/policy-1/test-policy-conf-includes-folder.conf.in ${CMAKE_CURRENT_BINARY_DIR}/policy-1/test-policy-conf-includes-folder.conf)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/policy-2/policy-photoserver-sasl.conf.in ${CMAKE_CURRENT_BINARY_DIR}/policy-2/policy-photoserver-sasl.conf)
+configure_file(${CMAKE_CURRENT_SOURCE_DIR}/policy-2/test-router-with-policy.json.in ${CMAKE_CURRENT_BINARY_DIR}/policy-2/test-router-with-policy.json)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/ssl_certs DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/displayname_files DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-1/management-access.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-1/)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-1/policy-boardwalk.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-1/)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-1/policy-safari.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-1/)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-2/policy-photoserver-sasl.sasldb DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-2)
# following install() functions will be called only if you do a make "install"
install(FILES ${SYSTEM_TEST_FILES}
@@ -114,4 +125,3 @@ install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/config-2
install(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ssl_certs
DESTINATION ${QPID_DISPATCH_HOME_INSTALLED}/tests)
-
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/config-1/A.json
----------------------------------------------------------------------
diff --git a/tests/config-1/A.json b/tests/config-1/A.json
new file mode 100644
index 0000000..01d62e1
--- /dev/null
+++ b/tests/config-1/A.json
@@ -0,0 +1,81 @@
+##
+## 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
+##
+
+
+[
+##
+## Container section - Configures the general operation of the AMQP container.
+##
+ ["container", {
+##
+## workerThreads - The number of threads that will be created to
+## process message traffic and other application work (timers, nonAmqp
+## file descriptors, etc.)
+##
+## The number of threads should be related to the number of available
+## processor cores. To fully utilize a quad-core system, set the
+## number of threads to 4.
+##
+ "workerThreads": 4,
+
+##
+## containerName - The name of the AMQP container. If not specified,
+## the container name will be set to a value of the container's
+## choosing. The automatically assigned container name is not
+## guaranteed to be persistent across restarts of the container.
+##
+ "containerName": "Qpid.Dispatch.Router.A"
+ }],
+
+ ["router", {
+ "mode": "standalone",
+ "routerId": "QDR"
+ }],
+
+##
+## Listeners and Connectors
+##
+ ["listener", {
+ "addr": "0.0.0.0",
+ "port": 20000,
+ "saslMechanisms": "ANONYMOUS"
+ }],
+ ["fixedAddress", {
+ "prefix": "/closest/",
+ "fanout": "single",
+ "bias": "closest"
+ }],
+
+ ["fixedAddress", {
+ "prefix": "/spread/",
+ "fanout": "single",
+ "bias": "spread"
+ }],
+
+ ["fixedAddress", {
+ "prefix": "/multicast/",
+ "fanout": "multiple"
+ }],
+
+ ["fixedAddress", {
+ "prefix": "/",
+ "fanout": "multiple"
+ }]
+]
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-1/management-access.json
----------------------------------------------------------------------
diff --git a/tests/policy-1/management-access.json b/tests/policy-1/management-access.json
new file mode 100644
index 0000000..960c4a1
--- /dev/null
+++ b/tests/policy-1/management-access.json
@@ -0,0 +1,45 @@
+##
+## 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
+##
+
+# A policy to allow unrestricted access to management
+# from host 0.0.0.0
+[
+ ["policyRuleset", {
+ "applicationName": "0.0.0.0",
+ "maxConnections": 50,
+ "maxConnPerUser": 5,
+ "maxConnPerHost": 20,
+ "connectionAllowDefault": true,
+ "settings": {
+ "default" : {
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSrc": true,
+ "allowAnonymousSender": true,
+ "sources": "$management",
+ "targets": "$management"
+ }
+ }
+ }
+ ]
+]
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-1/policy-boardwalk.json
----------------------------------------------------------------------
diff --git a/tests/policy-1/policy-boardwalk.json b/tests/policy-1/policy-boardwalk.json
new file mode 100644
index 0000000..174bdef
--- /dev/null
+++ b/tests/policy-1/policy-boardwalk.json
@@ -0,0 +1,96 @@
+##
+## 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
+##
+
+[
+ # The boardwalk policy ruleset
+ ["policyRuleset",
+ {
+ "applicationName": "boardwalk",
+ "maxConnections": 10,
+ "maxConnPerUser": 2,
+ "maxConnPerHost": 5,
+ "userGroups": {
+ "anonymous": "anonymous",
+ "users": "u1 u2 u3",
+ "superuser": "ellen"
+ },
+ "ingressHostGroups": {
+ "Ten18": "10.18.0.0-10.18.255.255",
+ "EllensWS": "72.135.2.9",
+ "TheLabs": "10.48.0.0-10.48.255.255, 192.168.0.0-192.168.255.255",
+ "Localhost": "127.0.0.1, ::1"
+ },
+ "ingressPolicies": {
+ "anonymous": "Ten18, TheLabs",
+ "superuser": "Localhost, EllensWS"
+ },
+ "connectionAllowDefault": true,
+ "settings": {
+ "anonymous": {
+ "maxFrameSize": 111111,
+ "maxMessageSize": 111111,
+ "maxSessionWindow": 111111,
+ "maxSessions": 1,
+ "maxSenders": 11,
+ "maxReceivers": 11,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public",
+ "targets": ""
+ },
+ "users": {
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public, private",
+ "targets": "public"
+ },
+ "superuser": {
+ "maxFrameSize": 666666,
+ "maxMessageSize": 666666,
+ "maxSessionWindow": 666666,
+ "maxSessions": 6,
+ "maxSenders": 66,
+ "maxReceivers": 66,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public, private, management, root",
+ "targets": "public, private, management, root"
+ },
+ "default": {
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public, private",
+ "targets": "public"
+ }
+ }
+ }
+ ]
+]
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-1/policy-safari.json
----------------------------------------------------------------------
diff --git a/tests/policy-1/policy-safari.json b/tests/policy-1/policy-safari.json
new file mode 100644
index 0000000..a61bbd9
--- /dev/null
+++ b/tests/policy-1/policy-safari.json
@@ -0,0 +1,96 @@
+##
+## 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
+##
+[
+ # The safari policy ruleset
+ ["policyRuleset",
+ {
+ "applicationName": "safari",
+ "maxConnections": 10,
+ "maxConnPerUser": 2,
+ "maxConnPerHost": 5,
+ "userGroups": {
+ "anonymous": "anonymous",
+ "clients": "moja mbili",
+ "guides": "kata hapa"
+ },
+ "ingressHostGroups": {
+ "basecamp": "72.135.2.9",
+ "mobile": "10.48.0.0-10.48.255.255, 192.168.0.0-192.168.255.255",
+ "Localhost": "127.0.0.1, ::1",
+ "TheWorld": "*"
+ },
+ "ingressPolicies": {
+ "anonymous": "TheWorld",
+ "clients": "basecamp",
+ "guides": "basecamp, mobile, Localhost"
+ },
+ "connectionAllowDefault": true,
+ "settings": {
+ "anonymous": {
+ "maxFrameSize": 111111,
+ "maxMessageSize": 111111,
+ "maxSessionWindow": 111111,
+ "maxSessions": 1,
+ "maxSenders": 11,
+ "maxReceivers": 11,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public",
+ "targets": ""
+ },
+ "clients": {
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public, private",
+ "targets": "public"
+ },
+ "guides": {
+ "maxFrameSize": 666666,
+ "maxMessageSize": 666666,
+ "maxSessionWindow": 666666,
+ "maxSessions": 6,
+ "maxSenders": 66,
+ "maxReceivers": 66,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public, private, management, root",
+ "targets": "public, private, management, root"
+ },
+ "default": {
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSrc": false,
+ "allowAnonymousSender": false,
+ "sources": "public, private",
+ "targets": "public"
+ }
+ }
+ }
+ ]
+]
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-1/test-policy-conf-includes-folder.conf.in
----------------------------------------------------------------------
diff --git a/tests/policy-1/test-policy-conf-includes-folder.conf.in b/tests/policy-1/test-policy-conf-includes-folder.conf.in
new file mode 100644
index 0000000..b6f0413
--- /dev/null
+++ b/tests/policy-1/test-policy-conf-includes-folder.conf.in
@@ -0,0 +1,64 @@
+##
+## 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
+##
+
+
+##
+## Container section - Configures the general operation of the AMQP container.
+##
+container {
+ ##
+ ## workerThreads - The number of threads that will be created to
+ ## process message traffic and other application work (timers, nonAmqp
+ ## file descriptors, etc.)
+ ##
+ ## The number of threads should be related to the number of available
+ ## processor cores. To fully utilize a quad-core system, set the
+ ## number of threads to 4.
+ ##
+ workerThreads: 4
+
+ ##
+ ## containerName - The name of the AMQP container. If not specified,
+ ## the container name will be set to a value of the container's
+ ## choosing. The automatically assigned container name is not
+ ## guaranteed to be persistent across restarts of the container.
+ ##
+ containerName: Qpid.Dispatch.Router.A
+}
+
+router {
+ mode: standalone
+ routerId: QDR
+}
+
+
+##
+## Listeners and Connectors
+##
+listener {
+ addr: 0.0.0.0
+ port: 22000
+ saslMechanisms: ANONYMOUS
+}
+
+policy {
+ maximumConnections: 10
+ enableAccessRules: true
+ policyFolder: ${CMAKE_CURRENT_BINARY_DIR}/policy-1
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-2/make-sasl.sh
----------------------------------------------------------------------
diff --git a/tests/policy-2/make-sasl.sh b/tests/policy-2/make-sasl.sh
new file mode 100755
index 0000000..d56d529
--- /dev/null
+++ b/tests/policy-2/make-sasl.sh
@@ -0,0 +1,63 @@
+#!/bin/bash -ex
+#
+# 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.
+#
+
+#
+# Generate sasl files for policy tests using test setup for 'photoserver'
+# This file is used to generate the files which are then committed to git
+# and distributed in the make install.
+#
+# sasldb file is generated and copied to tests/policy-2/ by cmake
+#
+export sasl_file=./policy-photoserver-sasl.sasldb
+
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd anonymous
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd u1
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd u2
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd p1
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd p2
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd zeke
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd ynot
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd alice
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd bob
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd ellen
+echo password | saslpasswd2 -c -p -f ${sasl_file} -u qdrouterd charlie
+
+echo password | saslpasswd2 -c -p -f ${sasl_file} anonymous
+echo password | saslpasswd2 -c -p -f ${sasl_file} u1
+echo password | saslpasswd2 -c -p -f ${sasl_file} u2
+echo password | saslpasswd2 -c -p -f ${sasl_file} p1
+echo password | saslpasswd2 -c -p -f ${sasl_file} p2
+echo password | saslpasswd2 -c -p -f ${sasl_file} zeke
+echo password | saslpasswd2 -c -p -f ${sasl_file} ynot
+echo password | saslpasswd2 -c -p -f ${sasl_file} alice
+echo password | saslpasswd2 -c -p -f ${sasl_file} bob
+echo password | saslpasswd2 -c -p -f ${sasl_file} ellen
+echo password | saslpasswd2 -c -p -f ${sasl_file} charlie
+
+sasldblistusers2 -f ${sasl_file}
+
+# Make sasl conf file
+# sasl.conf is generated and 'config'd by cmake
+cat > ./policy-photoserver-sasl.conf.in << "EOF"
+pwcheck_method: auxprop
+auxprop_plugin: sasldb
+sasldb_path: ${CMAKE_CURRENT_BINARY_DIR}/policy-2/policy-photoserver-sasl.sasldb
+mech_list: PLAIN ANONYMOUS
+EOF
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-2/policy-photoserver-sasl.conf.in
----------------------------------------------------------------------
diff --git a/tests/policy-2/policy-photoserver-sasl.conf.in b/tests/policy-2/policy-photoserver-sasl.conf.in
new file mode 100644
index 0000000..e9650b6
--- /dev/null
+++ b/tests/policy-2/policy-photoserver-sasl.conf.in
@@ -0,0 +1,4 @@
+pwcheck_method: auxprop
+auxprop_plugin: sasldb
+sasldb_path: ${CMAKE_CURRENT_BINARY_DIR}/policy-2/policy-photoserver-sasl.sasldb
+mech_list: PLAIN ANONYMOUS
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-2/policy-photoserver-sasl.sasldb
----------------------------------------------------------------------
diff --git a/tests/policy-2/policy-photoserver-sasl.sasldb b/tests/policy-2/policy-photoserver-sasl.sasldb
new file mode 100644
index 0000000..f145b16
Binary files /dev/null and b/tests/policy-2/policy-photoserver-sasl.sasldb differ
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/48e81620/tests/policy-2/ssl_certs/gencerts.sh
----------------------------------------------------------------------
diff --git a/tests/policy-2/ssl_certs/gencerts.sh b/tests/policy-2/ssl_certs/gencerts.sh
new file mode 100755
index 0000000..faca0a2
--- /dev/null
+++ b/tests/policy-2/ssl_certs/gencerts.sh
@@ -0,0 +1,39 @@
+#!/bin/bash -ex
+
+#
+# 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.
+#
+
+export SERVER=A1.Good.Server.domain.com
+export CLIENT=127.0.0.1
+
+keytool -storetype pkcs12 -keystore ca.pkcs12 -storepass ca-password -alias ca -keypass ca-password -genkey -dname "O=Trust Me Inc.,CN=Trusted.CA.com" -validity 99999
+openssl pkcs12 -nokeys -passin pass:ca-password -in ca.pkcs12 -passout pass:ca-password -out ca-certificate.pem
+
+keytool -storetype pkcs12 -keystore bad-ca.pkcs12 -storepass bad-ca-password -alias bad-ca -keypass bad-ca-password -genkey -dname "O=Trust Me Inc.,CN=Trusted.CA.com" -validity 99999
+openssl pkcs12 -nokeys -passin pass:bad-ca-password -in bad-ca.pkcs12 -passout pass:bad-ca-password -out bad-ca-certificate.pem
+
+keytool -storetype pkcs12 -keystore server.pkcs12 -storepass server-password -alias server-certificate -keypass server-password -genkey -dname "O=Server,CN=$SERVER" -validity 99999
+keytool -storetype pkcs12 -keystore server.pkcs12 -storepass server-password -alias server-certificate -keypass server-password -certreq -file server-request.pem
+keytool -storetype pkcs12 -keystore ca.pkcs12 -storepass ca-password -alias ca -keypass ca-password -gencert -rfc -validity 99999 -infile server-request.pem -outfile server-certificate.pem
+openssl pkcs12 -nocerts -passin pass:server-password -in server.pkcs12 -passout pass:server-password -out server-private-key.pem
+
+keytool -storetype pkcs12 -keystore client.pkcs12 -storepass client-password -alias client-certificate -keypass client-password -genkey -dname "O=Client,CN=$CLIENT" -validity 99999
+keytool -storetype pkcs12 -keystore client.pkcs12 -storepass client-password -alias client-certificate -keypass client-password -certreq -file client-request.pem
+keytool -storetype pkcs12 -keystore ca.pkcs12 -storepass ca-password -alias ca -keypass ca-password -gencert -rfc -validity 99999 -infile client-request.pem -outfile client-certificate.pem
+openssl pkcs12 -nocerts -passin pass:client-password -in client.pkcs12 -passout pass:client-password -out client-private-key.pem
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org