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/21 22:42:23 UTC
qpid-dispatch git commit: Added policy support code for link name
matching.
Repository: qpid-dispatch
Updated Branches:
refs/heads/crolke-DISPATCH-188-1 25af9cd7a -> 1aea48edc
Added policy support code for link name matching.
Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/1aea48ed
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/1aea48ed
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/1aea48ed
Branch: refs/heads/crolke-DISPATCH-188-1
Commit: 1aea48edcfffa35cb47c67babc294594087ace64
Parents: 25af9cd
Author: Chuck Rolke <cr...@redhat.com>
Authored: Mon Mar 21 17:33:50 2016 -0400
Committer: Chuck Rolke <cr...@redhat.com>
Committed: Mon Mar 21 17:33:50 2016 -0400
----------------------------------------------------------------------
src/policy.c | 142 +++++++++++++++++++++++++++++++++++---------
src/policy_internal.h | 100 +++++++++++++++++++++++++++++++
tests/CMakeLists.txt | 1 +
tests/policy_test.c | 89 +++++++++++++++++++++++++++
tests/run_unit_tests.c | 2 +
5 files changed, 306 insertions(+), 28 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/1aea48ed/src/policy.c
----------------------------------------------------------------------
diff --git a/src/policy.c b/src/policy.c
index 075e89b..0cb96b3 100644
--- a/src/policy.c
+++ b/src/policy.c
@@ -20,36 +20,21 @@
#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 "qpid/dispatch/message.h"
-#include <proton/engine.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>
-#include "qpid/dispatch/ctools.h"
-#include "qpid/dispatch/hash.h"
-#include "qpid/dispatch/threading.h"
-#include "qpid/dispatch/iterator.h"
-#include "qpid/dispatch/log.h"
-/**
- * Private Function Prototypes
- */
-void qd_policy_private_deny_amqp_connection(pn_connection_t *conn, const char *cond_name, const char *cond_descr);
-void qd_policy_deny_amqp_session(pn_session_t *ssn, qd_connection_t *qd_conn);
-void _qd_policy_deny_amqp_link(pn_link_t *link, qd_connection_t *qd_conn, char * s_or_r);
-void _qd_policy_deny_amqp_sender_link(pn_link_t *pn_link, qd_connection_t *qd_conn);
-void _qd_policy_deny_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_conn);
-
//
// TODO: when policy dev is more complete lower the log level
//
@@ -389,13 +374,9 @@ bool qd_policy_open_lookup_user(
return res;
}
-/** 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)
{
pn_condition_t * cond = pn_connection_condition(conn);
@@ -405,11 +386,8 @@ void qd_policy_private_deny_amqp_connection(pn_connection_t *conn, const char *c
}
-/** Internal function to deny an amqp session
- * The session is closed with a condition and the denial is counted.
- * @param[in,out] ssn proton session
- * @param[in,out] qd_conn dispatch connection
- */
+//
+//
void qd_policy_deny_amqp_session(pn_session_t *ssn, qd_connection_t *qd_conn)
{
pn_condition_t * cond = pn_session_condition(ssn);
@@ -503,6 +481,114 @@ void _qd_policy_deny_amqp_receiver_link(pn_link_t *pn_link, qd_connection_t *qd_
//
//
+#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)
{
if (qd_conn->policy_settings->maxSenders) {
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/1aea48ed/src/policy_internal.h
----------------------------------------------------------------------
diff --git a/src/policy_internal.h b/src/policy_internal.h
new file mode 100644
index 0000000..1c00ab8
--- /dev/null
+++ b/src/policy_internal.h
@@ -0,0 +1,100 @@
+#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
+ *
+ * @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}'
+ * @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/1aea48ed/tests/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e752c2a..c779b9a 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
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/1aea48ed/tests/policy_test.c
----------------------------------------------------------------------
diff --git a/tests/policy_test.c b/tests/policy_test.c
new file mode 100644
index 0000000..0061169
--- /dev/null
+++ b/tests/policy_test.c
@@ -0,0 +1,89 @@
+/*
+ * 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 "test_case.h"
+#include <stdio.h>
+#include <string.h>
+#include "policy.h"
+#include "policy_internal.h"
+
+static char *test_link_name_lookup(void *context)
+{
+ // Degenerate blank names
+ if (_qd_policy_approve_link_name("a", "a", ""))
+ return "blank proposed name not rejected";
+ if (_qd_policy_approve_link_name("a", "", "a"))
+ return "blank allowed list not rejected";
+
+ // Easy matches
+ if (!_qd_policy_approve_link_name("", "joe", "joe"))
+ return "proposed link 'joe' should match allowed links 'joe' but does not";
+ if (_qd_policy_approve_link_name("", "joe", "joey"))
+ return "proposed link 'joey' should not match allowed links 'joe' but does";
+
+ // Wildcard matches
+ if (!_qd_policy_approve_link_name("", "joe*", "joey"))
+ return "proposed link 'joey' should match allowed links 'joe*' but does not";
+ if (!_qd_policy_approve_link_name("", "joe*", "joezzzZZZ"))
+ return "proposed link 'joezzzZZZ' should match allowed links 'joe*' but does not";
+ if (!_qd_policy_approve_link_name("", "joe,*", "joey"))
+ return "proposed link 'joey' should match allowed links 'joe,*' but does not";
+
+ // Deeper match
+ if (!_qd_policy_approve_link_name("", "no1,no2,no3,yes,no4", "yes"))
+ return "proposed link 'yes' should match allowed links 'no1,no2,no3,yes,no4' but does not";
+
+ // Deeeper match - triggers malloc/free internal handler
+ char * bufp = (char *)malloc(512 * 5 + 6);
+ char * wp = bufp;
+ int i;
+ for (i=0; i<512; i++) {
+ wp += sprintf(wp, "n%03d,", i);
+ }
+ sprintf(wp, "yes");
+ if (!_qd_policy_approve_link_name("", bufp, "yes")) {
+ free(bufp);
+ return "proposed link 'yes' should match allowed large list but does not";
+ }
+ free(bufp);
+
+ // Substitute a user name
+ if (!_qd_policy_approve_link_name("chuck", "ab${user}xyz", "abchuckxyz"))
+ return "proposed link 'abchuckxyz' should match allowed links with ${user} but does not";
+ if (!_qd_policy_approve_link_name("chuck", "${user}xyz", "chuckxyz"))
+ return "proposed link 'chuckxyz' should match allowed links with ${user} but does not";
+ if (!_qd_policy_approve_link_name("chuck", "ab${user}", "abchuck"))
+ return "proposed link 'abchuck' should match allowed links with ${user} but does not";
+
+ // Combine user name and wildcard
+ if (!_qd_policy_approve_link_name("chuck", "ab${user}*", "abchuckzyxw"))
+ return "proposed link 'abchuckzyxw' should match allowed links with ${user}* but does not";
+
+ return 0;
+}
+
+int policy_tests(void)
+{
+ int result = 0;
+
+ TEST_CASE(test_link_name_lookup, 0);
+
+ return result;
+}
+
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/1aea48ed/tests/run_unit_tests.c
----------------------------------------------------------------------
diff --git a/tests/run_unit_tests.c b/tests/run_unit_tests.c
index d2e5571..df4fe56 100644
--- a/tests/run_unit_tests.c
+++ b/tests/run_unit_tests.c
@@ -29,6 +29,7 @@ int alloc_tests(void);
int server_tests(qd_dispatch_t *qd);
int parse_tests(void);
int compose_tests(void);
+int policy_tests(void);
int main(int argc, char** argv)
{
@@ -53,6 +54,7 @@ int main(int argc, char** argv)
#if USE_MEMORY_POOL
result += alloc_tests();
#endif
+ result += policy_tests();
qd_dispatch_free(qd); // dispatch_free last.
return result;
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org