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/09/19 21:23:47 UTC
qpid-dispatch git commit: DISPATCH-8: Authenticate message user-id on
ingress
Repository: qpid-dispatch
Updated Branches:
refs/heads/master 39b349a36 -> 6be6e4610
DISPATCH-8: Authenticate message user-id on ingress
Add proxy check enable setting per vhost user group.
Verify proxy is allowed for incoming messages.
Add self tests to demonstrate proxy check rejecting messages or not.
Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/6be6e461
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/6be6e461
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/6be6e461
Branch: refs/heads/master
Commit: 6be6e461040808ec9aed75b3213a2f03496a510b
Parents: 39b349a
Author: Chuck Rolke <cr...@redhat.com>
Authored: Mon Sep 19 17:18:32 2016 -0400
Committer: Chuck Rolke <cr...@redhat.com>
Committed: Mon Sep 19 17:18:32 2016 -0400
----------------------------------------------------------------------
doc/book/policy.adoc | 1 +
.../qdrouter.policyRuleset.settings.txt | 7 +
.../policy/policy_local.py | 6 +-
src/policy.c | 1 +
src/policy.h | 1 +
src/router_node.c | 32 +-
tests/CMakeLists.txt | 2 +
tests/policy-4/management-access.json | 104 ++++++
tests/system_tests_user_id.py | 12 +
tests/system_tests_user_id_proxy.py | 315 +++++++++++++++++++
10 files changed, 479 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/doc/book/policy.adoc
----------------------------------------------------------------------
diff --git a/doc/book/policy.adoc b/doc/book/policy.adoc
index 9d41323..84a5b91 100644
--- a/doc/book/policy.adoc
+++ b/doc/book/policy.adoc
@@ -160,6 +160,7 @@ This object is the data value contained in entries in the policy/groups map.
| maxReceivers | 2^31-1 | Maximum number of receiving links that may be created on this connection.
| allowDynamicSource | false | This connection is allowed to create receiving links using the Dynamic Link Source feature.
| allowAnonymousSender | false | This connection is allowed to create sending links using the Anonymous Sender feature.
+| allowUserIdProxy | false | This connection is allowed to send messages with a user_id property that differs from the connection's authenticated user id.
| sources | "" | List of Source addresses allowed when creating receiving links. This list may be expressed as a CSV string or as a list of strings. An empty list denies all access.
| targets | "" | List of Target addresses allowed when creating sending links. This list may be expressed as a CSV string or as a list of strings. An empty list denies all access.
|====
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/python/qpid_dispatch/management/qdrouter.policyRuleset.settings.txt
----------------------------------------------------------------------
diff --git a/python/qpid_dispatch/management/qdrouter.policyRuleset.settings.txt b/python/qpid_dispatch/management/qdrouter.policyRuleset.settings.txt
index c0c4208..798e146 100644
--- a/python/qpid_dispatch/management/qdrouter.policyRuleset.settings.txt
+++ b/python/qpid_dispatch/management/qdrouter.policyRuleset.settings.txt
@@ -95,6 +95,13 @@ Until the schema is extended specify embedded maps this document describes the v
"required": false,
"create": true
},
+ "allowUserIdProxy": {
+ "type": "boolean",
+ "description": "This connection is allowed to send messages with a user_id property that differs from the connection authenticated user name.",
+ "default": false,
+ "required": false,
+ "create": true
+ },
"sources": {
"type": "string",
"description": "List of Source addresses allowed when creating receiving links.",
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/python/qpid_dispatch_internal/policy/policy_local.py
----------------------------------------------------------------------
diff --git a/python/qpid_dispatch_internal/policy/policy_local.py b/python/qpid_dispatch_internal/policy/policy_local.py
index 87fe11a..8da5402 100644
--- a/python/qpid_dispatch_internal/policy/policy_local.py
+++ b/python/qpid_dispatch_internal/policy/policy_local.py
@@ -59,6 +59,7 @@ class PolicyKeys(object):
KW_MAX_RECEIVERS = "maxReceivers"
KW_ALLOW_DYNAMIC_SRC = "allowDynamicSource"
KW_ALLOW_ANONYMOUS_SENDER = "allowAnonymousSender"
+ KW_ALLOW_USERID_PROXY = "allowUserIdProxy"
KW_SOURCES = "sources"
KW_TARGETS = "targets"
@@ -118,6 +119,7 @@ class PolicyCompiler(object):
PolicyKeys.KW_MAX_RECEIVERS,
PolicyKeys.KW_ALLOW_DYNAMIC_SRC,
PolicyKeys.KW_ALLOW_ANONYMOUS_SENDER,
+ PolicyKeys.KW_ALLOW_USERID_PROXY,
PolicyKeys.KW_SOURCES,
PolicyKeys.KW_TARGETS
]
@@ -219,6 +221,7 @@ class PolicyCompiler(object):
policy_out[PolicyKeys.KW_MAX_RECEIVERS] = 2147483647
policy_out[PolicyKeys.KW_ALLOW_DYNAMIC_SRC] = False
policy_out[PolicyKeys.KW_ALLOW_ANONYMOUS_SENDER] = False
+ policy_out[PolicyKeys.KW_ALLOW_USERID_PROXY] = False
policy_out[PolicyKeys.KW_SOURCES] = ''
policy_out[PolicyKeys.KW_TARGETS] = ''
@@ -247,7 +250,8 @@ class PolicyCompiler(object):
return False
policy_out[key] = val_out
elif key in [PolicyKeys.KW_ALLOW_ANONYMOUS_SENDER,
- PolicyKeys.KW_ALLOW_DYNAMIC_SRC
+ PolicyKeys.KW_ALLOW_DYNAMIC_SRC,
+ PolicyKeys.KW_ALLOW_USERID_PROXY
]:
if not type(val) is bool:
errors.append("Policy vhost '%s' user group '%s' option '%s' has illegal boolean value '%s'." %
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/src/policy.c
----------------------------------------------------------------------
diff --git a/src/policy.c b/src/policy.c
index 76f863c..a347c12 100644
--- a/src/policy.c
+++ b/src/policy.c
@@ -337,6 +337,7 @@ bool qd_policy_open_lookup_user(
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->allowDynamicSource = qd_entity_opt_bool((qd_entity_t*)upolicy, "allowDynamicSource", false);
+ settings->allowUserIdProxy = qd_entity_opt_bool((qd_entity_t*)upolicy, "allowUserIdProxy", 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*)
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/src/policy.h
----------------------------------------------------------------------
diff --git a/src/policy.h b/src/policy.h
index 6287bfb..c89ca2b 100644
--- a/src/policy.h
+++ b/src/policy.h
@@ -50,6 +50,7 @@ struct qd_policy__settings_s {
int maxReceivers;
bool allowDynamicSource;
bool allowAnonymousSender;
+ bool allowUserIdProxy;
char *sources;
char *targets;
qd_policy_denial_counts_t *denialCounts;
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/src/router_node.c
----------------------------------------------------------------------
diff --git a/src/router_node.c b/src/router_node.c
index 3dcf960..115bf2e 100644
--- a/src/router_node.c
+++ b/src/router_node.c
@@ -264,6 +264,18 @@ static void AMQP_rx_handler(void* context, qd_link_t *link, pn_delivery_t *pnd)
bool anonymous_link = qdr_link_is_anonymous(rlink);
//
+ // Determine if the user of this connection is allowed to proxy the
+ // user_id of messages. A message user_id is proxied when the
+ // property value differs from the authenticated user name of the connection.
+ // If the user is not allowed to proxy the user_id then the message user_id
+ // must be blank or it must be equal to the connection user name.
+ //
+ bool check_user = false;
+ qd_connection_t *conn = qd_link_connection(link);
+ if (conn->policy_settings)
+ check_user = !conn->policy_settings->allowUserIdProxy;
+
+ //
// Validate the content of the delivery as an AMQP message. This is done partially, only
// to validate that we can find the fields we need to route the message.
//
@@ -271,10 +283,28 @@ static void AMQP_rx_handler(void* context, qd_link_t *link, pn_delivery_t *pnd)
// 'to' field. If the link is not anonymous, we don't need the 'to' field as we will be
// using the address from the link target.
//
- qd_message_depth_t validation_depth = anonymous_link ? QD_DEPTH_PROPERTIES : QD_DEPTH_MESSAGE_ANNOTATIONS;
+ qd_message_depth_t validation_depth = (anonymous_link || check_user) ? QD_DEPTH_PROPERTIES : QD_DEPTH_MESSAGE_ANNOTATIONS;
bool valid_message = qd_message_check(msg, validation_depth);
if (valid_message) {
+ if (check_user) {
+ // This connection must not allow proxied user_id
+ qd_field_iterator_t *userid_iter = qd_message_field_iterator(msg, QD_FIELD_USER_ID);
+ if (userid_iter) {
+ // The user_id property has been specified
+ if (qd_field_iterator_remaining(userid_iter) > 0) {
+ // user_id property in message is not blank
+ if (!qd_field_iterator_equal(userid_iter, (const unsigned char *)conn->user_id)) {
+ // This message is rejected: attempted user proxy is disallowed
+ qd_log(router->log_source, QD_LOG_DEBUG, "Message rejected due to user_id proxy violation. User:%s", conn->user_id);
+ pn_delivery_update(pnd, PN_REJECTED);
+ pn_delivery_settle(pnd);
+ return;
+ }
+ }
+ }
+ }
+
qd_parsed_field_t *in_ma = qd_message_message_annotations(msg);
qd_bitmask_t *link_exclusions;
bool strip = qdr_link_strip_annotations_in(rlink);
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 5ab3a0f..eb04e82 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -82,6 +82,7 @@ foreach(py_test_module
system_tests_qdstat
system_tests_sasl_plain
system_tests_user_id
+ system_tests_user_id_proxy
system_tests_deprecated
system_tests_two_routers)
@@ -110,6 +111,7 @@ file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-1/policy-boardwalk.json DESTINATI
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)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-3/test-sender-receiver-limits.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-3)
+file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/policy-4/management-access.json DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/policy-4/)
# following install() functions will be called only if you do a make "install"
install(FILES ${SYSTEM_TEST_FILES}
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/tests/policy-4/management-access.json
----------------------------------------------------------------------
diff --git a/tests/policy-4/management-access.json b/tests/policy-4/management-access.json
new file mode 100644
index 0000000..708f547
--- /dev/null
+++ b/tests/policy-4/management-access.json
@@ -0,0 +1,104 @@
+##
+## 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 - proton 0.12
+# localhost - proton 0.13
+# unnamed host- proton 0.13
+#
+# These vhosts deny user_id proxy.
+#
+[
+ ["vhost", {
+ "id": "",
+ "maxConnections": 50,
+ "maxConnectionsPerUser": 5,
+ "maxConnectionsPerHost": 20,
+ "allowUnknownUser": true,
+ "groups": {
+ "$default" : {
+ "users": "*",
+ "remoteHosts": "*",
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSource": true,
+ "allowAnonymousSender": true,
+ "allowUserIdProxy": false,
+ "sources": "$management, _local/$displayname",
+ "targets": "$management, _local/$displayname"
+ }
+ }
+ }
+ ],
+ ["vhost", {
+ "id": "0.0.0.0",
+ "maxConnections": 50,
+ "maxConnectionsPerUser": 5,
+ "maxConnectionsPerHost": 20,
+ "allowUnknownUser": true,
+ "groups": {
+ "$default" : {
+ "users": "*",
+ "remoteHosts": "*",
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSource": true,
+ "allowAnonymousSender": true,
+ "allowUserIdProxy": false,
+ "sources": "$management, _local/$displayname",
+ "targets": "$management, _local/$displayname"
+ }
+ }
+ }
+ ],
+ ["vhost", {
+ "id": "localhost",
+ "maxConnections": 50,
+ "maxConnectionsPerUser": 5,
+ "maxConnectionsPerHost": 20,
+ "allowUnknownUser": true,
+ "groups": {
+ "$default" : {
+ "users": "*",
+ "remoteHosts": "*",
+ "maxFrameSize": 222222,
+ "maxMessageSize": 222222,
+ "maxSessionWindow": 222222,
+ "maxSessions": 2,
+ "maxSenders": 22,
+ "maxReceivers": 22,
+ "allowDynamicSource": true,
+ "allowAnonymousSender": true,
+ "allowUserIdProxy": false,
+ "sources": "$management, _local/$displayname",
+ "targets": "$management, _local/$displayname"
+ }
+ }
+ }
+ ]
+]
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/tests/system_tests_user_id.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_user_id.py b/tests/system_tests_user_id.py
index ddd4f67..7f40b4d 100644
--- a/tests/system_tests_user_id.py
+++ b/tests/system_tests_user_id.py
@@ -363,6 +363,18 @@ class QdSSLUseridTest(TestCase):
M1.get(rm)
self.assertEqual('12345', rm.body['user_name'])
+ tm = Message()
+ rm = Message()
+ tm.address = addr
+ tm.reply_to = reply_to
+ tm.user_id = "bad-user-id" # policy is disabled; user proxy is allowed
+ tm.body = {'profilename': 'server-ssl10', 'opcode': 'QUERY', 'userid': '12345'}
+ M1.put(tm)
+ M1.send()
+ M1.recv(1)
+ M1.get(rm)
+ self.assertEqual('12345', rm.body['user_name'])
+
M1.stop()
node.close()
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/6be6e461/tests/system_tests_user_id_proxy.py
----------------------------------------------------------------------
diff --git a/tests/system_tests_user_id_proxy.py b/tests/system_tests_user_id_proxy.py
new file mode 100644
index 0000000..b82617b
--- /dev/null
+++ b/tests/system_tests_user_id_proxy.py
@@ -0,0 +1,315 @@
+#
+# 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
+#
+
+
+import os
+import unittest
+from system_test import TestCase, Qdrouterd, DIR, main_module
+from qpid_dispatch.management.client import Node
+import proton
+from proton import SSLDomain, Message, ProtonException, Delivery
+from proton.utils import BlockingConnection
+
+class QdSSLUseridTest(TestCase):
+
+ @staticmethod
+ def ssl_file(name):
+ return os.path.join(DIR, 'ssl_certs', name)
+
+ @classmethod
+ def setUpClass(cls):
+ super(QdSSLUseridTest, cls).setUpClass()
+
+ ssl_profile1_json = os.path.join(DIR, 'displayname_files', 'profile_names1.json')
+ ssl_profile2_json = os.path.join(DIR, 'displayname_files', 'profile_names2.json')
+ policy_config_path = os.path.join(DIR, 'policy-4')
+
+ config = Qdrouterd.Config([
+ ('router', {'id': 'QDR', 'workerThreads': 1}),
+
+ ('policy', {'maxConnections': 20, 'policyDir': policy_config_path, 'enableVhostPolicy': 'true'}),
+
+ # sha1
+ ('sslProfile', {'name': 'server-ssl1',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '1',
+ 'password': 'server-password'}),
+
+ # sha256
+ ('sslProfile', {'name': 'server-ssl2',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '2',
+ 'password': 'server-password'}),
+
+ # sha512
+ ('sslProfile', {'name': 'server-ssl3',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '5',
+ 'password': 'server-password'}),
+
+ # sha256 combination
+ ('sslProfile', {'name': 'server-ssl4',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '2noucs',
+ 'password': 'server-password'}),
+
+ # sha1 combination
+ ('sslProfile', {'name': 'server-ssl5',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '1cs',
+ 'password': 'server-password'}),
+
+ # sha512 combination
+ ('sslProfile', {'name': 'server-ssl6',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': 'cs5',
+ 'password': 'server-password'}),
+
+ # no fingerprint field
+ ('sslProfile', {'name': 'server-ssl7',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': 'nsuco',
+ 'password': 'server-password'}),
+
+ # no fingerprint field variation
+ ('sslProfile', {'name': 'server-ssl8',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': 'scounl',
+ 'password': 'server-password'}),
+
+ #no uidFormat
+ ('sslProfile', {'name': 'server-ssl9',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'password': 'server-password'}),
+
+ # one component of uidFormat is invalid (x), the unrecognized component will be ignored,
+ # this will be treated like 'uidFormat': '1'
+ ('sslProfile', {'name': 'server-ssl10',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '1x',
+ 'displayNameFile': ssl_profile2_json,
+ 'password': 'server-password'}),
+
+ # All components in the uidFormat are unrecognized, pn_get_transport_user will be returned
+ ('sslProfile', {'name': 'server-ssl11',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': 'abxd',
+ 'password': 'server-password'}),
+
+ ('sslProfile', {'name': 'server-ssl12',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '1',
+ 'displayNameFile': ssl_profile1_json,
+ 'password': 'server-password'}),
+
+ # should translate a display name
+ ('sslProfile', {'name': 'server-ssl13',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '2',
+ 'displayNameFile': ssl_profile2_json,
+ 'password': 'server-password'}),
+
+ ('sslProfile', {'name': 'server-ssl14',
+ 'certDb': cls.ssl_file('ca-certificate.pem'),
+ 'certFile': cls.ssl_file('server-certificate.pem'),
+ 'keyFile': cls.ssl_file('server-private-key.pem'),
+ 'uidFormat': '1',
+ 'displayNameFile': ssl_profile1_json,
+ 'password': 'server-password'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl1', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl2', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl3', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl4', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl5', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl6', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl7', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl8', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl9', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl10', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl11', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ # peer is not being authenticated here. the user must "anonymous" which is what pn_transport_get_user
+ # returns
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl12', 'authenticatePeer': 'no',
+ 'requireSsl': 'yes', 'saslMechanisms': 'ANONYMOUS'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl13', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'sslProfile': 'server-ssl14', 'authenticatePeer': 'yes',
+ 'requireSsl': 'yes', 'saslMechanisms': 'EXTERNAL'}),
+
+ ('listener', {'port': cls.tester.get_port(), 'authenticatePeer': 'no'})
+
+ ])
+
+ cls.router = cls.tester.qdrouterd('ssl-test-router', config, wait=True)
+
+ def address(self, index):
+ return self.router.addresses[index]
+
+ def create_ssl_domain(self, ssl_options_dict, mode=SSLDomain.MODE_CLIENT):
+ """Return proton.SSLDomain from command line options or None if no SSL options specified.
+ @param opts: Parsed optoins including connection_options()
+ """
+ certificate, key, trustfile, password = ssl_options_dict.get('ssl-certificate'), \
+ ssl_options_dict.get('ssl-key'), \
+ ssl_options_dict.get('ssl-trustfile'), \
+ ssl_options_dict.get('ssl-password')
+
+ if not (certificate or trustfile):
+ return None
+ domain = SSLDomain(mode)
+ if trustfile:
+ domain.set_trusted_ca_db(str(trustfile))
+ domain.set_peer_authentication(SSLDomain.VERIFY_PEER, str(trustfile))
+ if certificate:
+ domain.set_credentials(str(certificate), str(key), str(password))
+
+ return domain
+
+class QdSSLUseridProxy(QdSSLUseridTest):
+
+ def test_message_user_id_proxy_bad_name_disallowed(self):
+ ssl_opts = dict()
+ ssl_opts['ssl-trustfile'] = self.ssl_file('ca-certificate.pem')
+ ssl_opts['ssl-certificate'] = self.ssl_file('client-certificate.pem')
+ ssl_opts['ssl-key'] = self.ssl_file('client-private-key.pem')
+ ssl_opts['ssl-password'] = 'client-password'
+
+ # create the SSL domain object
+ domain = self.create_ssl_domain(ssl_opts)
+
+ # Send a message with bad user_id. This message should be rejected.
+ # Connection has user_id 'user13'.
+ addr = self.address(13).replace("amqp", "amqps")
+ blocking_connection = BlockingConnection(addr, ssl_domain=domain)
+ blocking_sender = blocking_connection.create_sender("$management")
+
+ request = proton.Message()
+ request.user_id = u"bad-user-id"
+
+ result = Delivery.ACCEPTED
+ try:
+ delivery = blocking_sender.send(request, timeout=10)
+ result = delivery.remote_state
+ except proton.utils.SendException as e:
+ result = e.state
+
+ self.assertTrue (result == Delivery.REJECTED,
+ "Router accepted a message with user_id that did not match connection user_id")
+
+ def test_message_user_id_proxy_blank_name_allowed(self):
+ # Send a message with a blank user_id that should be allowed
+ M1 = self.messenger()
+ M1.route("amqp:/*", self.address(14) + "/$1")
+
+ subscription = M1.subscribe("amqp:/#")
+
+ reply_to = subscription.address
+ addr = 'amqp:/_local/$displayname'
+
+ tm = Message()
+ rm = Message()
+ tm.address = addr
+ tm.reply_to = reply_to
+ tm.body = {'profilename': 'server-ssl10', 'opcode': 'QUERY',
+ 'userid': '94745961c5646ee0129536b3acef1eea0d8d2f26f8c353455233027bcd47'}
+ M1.put(tm)
+
+ M1.send()
+ M1.recv(1)
+ M1.get(rm)
+ self.assertEqual('elaine', rm.body['user_name'])
+
+ def test_message_user_id_proxy_correct_name_allowed(self):
+ # Send a message with a good user_id that should be allowed
+ M2 = self.messenger()
+ M2.route("amqp:/*", self.address(14) + "/$1")
+
+ subscription = M2.subscribe("amqp:/#")
+
+ reply_to = subscription.address
+ addr = 'amqp:/_local/$displayname'
+
+ tm = Message()
+ rm = Message()
+ tm.address = addr
+ tm.reply_to = reply_to
+ tm.user_id = "anonymous"
+ tm.body = {'profilename': 'server-ssl10', 'opcode': 'QUERY',
+ 'userid': '94745961c5646ee0129536b3acef1eea0d8d2f26f8c353455233027bcd47'}
+ M2.put(tm)
+
+ M2.send()
+ M2.recv(1)
+ M2.get(rm)
+ self.assertEqual('elaine', rm.body['user_name'])
+
+
+if __name__ == '__main__':
+ unittest.main(main_module())
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org