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 <[email protected]> Authored: Mon Mar 21 17:33:50 2016 -0400 Committer: Chuck Rolke <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
