From a02aad0a9c7b3b76e94cd44b3024ca644900a702 Mon Sep 17 00:00:00 2001 From: "Roger A. Light" Date: Thu, 8 Jul 2021 17:45:50 +0100 Subject: [PATCH] Add mosquitto_sub_matches_acl_with_pattern. --- ChangeLog.txt | 3 + include/mosquitto.h | 43 ++- lib/linker.version | 1 + lib/util_topic.c | 57 +++- src/linker-macosx.syms | 1 + src/linker.syms | 1 + test/unit/util_topic_test.c | 520 +++++++++++++++++++++++++++++++++++- 7 files changed, 609 insertions(+), 17 deletions(-) diff --git a/ChangeLog.txt b/ChangeLog.txt index cbb40de1..d2400ebf 100644 --- a/ChangeLog.txt +++ b/ChangeLog.txt @@ -52,6 +52,9 @@ Client library: substitution. - Add `mosquitto_sub_matches_acl()`, which can match one topic filter (a subscription) against another topic filter (an ACL). +- Add `mosquitto_sub_matches_acl_with_pattern()`, which can match one topic + filter (a subscription) against another topic filter (an ACL), with `%c` and + `%u` patterns for client id / username substitution. - Performance: reduce memory allocations when sending packets. Clients: diff --git a/include/mosquitto.h b/include/mosquitto.h index d2048130..3c8fe6e2 100644 --- a/include/mosquitto.h +++ b/include/mosquitto.h @@ -2463,7 +2463,7 @@ libmosq_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, * Check whether a topic matches a subscription, with client id/username * pattern substitution. * - * Any instances of a topic hierarchy that are exactly %c or %u will be + * Any instances of a subscriptions hierarchy that are exactly %c or %u will be * replaced with the client id or username respectively. * * For example: @@ -2515,7 +2515,46 @@ libmosq_EXPORT int mosquitto_topic_matches_sub_with_pattern(const char *sub, con * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if the input parameters were invalid. */ -libmosq_EXPORT int mosquitto_sub_matches_acl(const char *sub, const char *topic, bool *result); +libmosq_EXPORT int mosquitto_sub_matches_acl(const char *acl, const char *sub, bool *result); + + +/* + * Function: mosquitto_sub_matches_acl_with_pattern + * + * Check whether a subscription (a topic filter with wildcards) matches an ACL + * (a topic filter with wildcards) , with client id/username pattern + * substitution. + * + * Any instances of an ACL hierarchy that are exactly %c or %u will be + * replaced with the client id or username respectively. + * + * For example: + * + * mosquitto_sub_matches_acl_with_pattern("sensors/%c/+", "sensors/kitchen/temperature", "kitchen", NULL, &result) + * -> this will match + * + * mosquitto_sub_matches_acl_with_pattern("sensors/%c/+", "sensors/bathroom/temperature", "kitchen", NULL, &result) + * -> this will not match + * + * mosquitto_sub_matches_acl_with_pattern("sensors/%count/+", "sensors/kitchen/temperature", "kitchen", NULL, &result) + * -> this will not match - the `%count` is not treated as a pattern + * + * mosquitto_sub_matches_acl_with_pattern("%c/%c/%u/+", "kitchen/kitchen/bathroom/bathroom", "kitchen", "bathroom", &result) + * -> this will match + * + * Parameters: + * acl - ACL topic filter string to check sub against. + * sub - subscription to check. + * clientid - client id to substitute in patterns. If NULL, then any %c patterns will not match. + * username - username to substitute in patterns. If NULL, then any %u patterns will not match. + * result - bool pointer to hold result. Will be set to true if the subscription + * matches the ACL. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success + * MOSQ_ERR_INVAL - if the input parameters were invalid. + */ +libmosq_EXPORT int mosquitto_sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result); /* diff --git a/lib/linker.version b/lib/linker.version index 220c79b7..25ade8fc 100644 --- a/lib/linker.version +++ b/lib/linker.version @@ -147,4 +147,5 @@ MOSQ_2.1 { mosquitto_pre_connect_callback_set; mosquitto_topic_matches_sub_with_pattern; mosquitto_sub_matches_acl; + mosquitto_sub_matches_acl_with_pattern; } MOSQ_1.7; diff --git a/lib/util_topic.c b/lib/util_topic.c index 59548cc1..b9790f22 100644 --- a/lib/util_topic.c +++ b/lib/util_topic.c @@ -354,9 +354,11 @@ static int topic_matches_sub(const char *sub, const char *topic, const char *cli return MOSQ_ERR_SUCCESS; } -static int sub_matches_acl(const char *acl, const char *sub, bool *result) +static int sub_matches_acl(const char *acl, const char *sub, const char *clientid, const char *username, bool match_patterns, bool *result) { size_t apos; + const char *pattern_check; + const char *lastchar = NULL; *result = false; @@ -373,6 +375,51 @@ static int sub_matches_acl(const char *acl, const char *sub, bool *result) apos = 0; while(acl[0] != 0){ + if(match_patterns && + (lastchar == NULL || lastchar[0] == '/') && + acl[0] == '%' && + (acl[1] == 'c' || acl[1] == 'u') && + (acl[2] == '/' || acl[2] == '\0') + ){ + + if(acl[1] == 'c'){ + pattern_check = clientid; + }else{ + pattern_check = username; + } + if(pattern_check == NULL || pattern_check[0] == '\0'){ + /* no match */ + return MOSQ_ERR_SUCCESS; + } + if(pattern_check[1] == '\0' && ( + pattern_check[0] == '+' || + pattern_check[0] == '#' || + pattern_check[0] == '/') + ){ + + /* username/client id of just + / # not allowed */ + return MOSQ_ERR_SUCCESS; + } + + apos +=2; + acl += 2; + + while(pattern_check[0] != 0 && sub[0] != 0 && sub[0] != '/'){ + if(pattern_check[0] != sub[0]){ + /* Valid input, but no match */ + return MOSQ_ERR_SUCCESS; + } + pattern_check++; + sub++; + } + if(sub[0] == '\0' && (acl[0] == '\0' || + (acl[0] == '/' && acl[1] == '#' && acl[2] == '\0')) + ){ + + *result = true; + return MOSQ_ERR_SUCCESS; + } + } if(acl[0] != sub[0] || sub[0] == 0){ /* Check for wildcard matches */ if(acl[0] == '+'){ if(sub[0] == '#'){ @@ -460,6 +507,7 @@ static int sub_matches_acl(const char *acl, const char *sub, bool *result) return MOSQ_ERR_SUCCESS; } } + lastchar = acl-1; } if((sub[0] != 0 || acl[0] != 0)){ return MOSQ_ERR_SUCCESS; @@ -477,7 +525,12 @@ static int sub_matches_acl(const char *acl, const char *sub, bool *result) int mosquitto_sub_matches_acl(const char *acl, const char *sub, bool *result) { - return sub_matches_acl(acl, sub, result); + return sub_matches_acl(acl, sub, NULL, NULL, false, result); +} + +int mosquitto_sub_matches_acl_with_pattern(const char *acl, const char *sub, const char *clientid, const char *username, bool *result) +{ + return sub_matches_acl(acl, sub, clientid, username, true, result); } int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result) diff --git a/src/linker-macosx.syms b/src/linker-macosx.syms index a4d59935..40481bca 100644 --- a/src/linker-macosx.syms +++ b/src/linker-macosx.syms @@ -31,6 +31,7 @@ _mosquitto_realloc _mosquitto_set_username _mosquitto_strdup _mosquitto_sub_matches_acl +_mosquitto_sub_matches_acl_with_pattern _mosquitto_sub_topic_check _mosquitto_topic_matches_sub _mosquitto_topic_matches_sub_with_pattern diff --git a/src/linker.syms b/src/linker.syms index 120bb3cf..f1b8deb1 100644 --- a/src/linker.syms +++ b/src/linker.syms @@ -32,6 +32,7 @@ mosquitto_set_username; mosquitto_strdup; mosquitto_sub_matches_acl; + mosquitto_sub_matches_acl_with_pattern; mosquitto_sub_topic_check; mosquitto_topic_matches_sub; mosquitto_topic_matches_sub_with_pattern; diff --git a/test/unit/util_topic_test.c b/test/unit/util_topic_test.c index 8ceb2c16..f8a8ee7d 100644 --- a/test/unit/util_topic_test.c +++ b/test/unit/util_topic_test.c @@ -84,7 +84,7 @@ static void TEST_empty_input(void) CU_ASSERT_EQUAL(match, false); } -static void TEST_pattern_empty_input(void) +static void TEST_topic_pattern_empty_input(void) { int rc; bool match; @@ -150,6 +150,72 @@ static void TEST_pattern_empty_input(void) CU_ASSERT_EQUAL(match, false); } +static void TEST_acl_pattern_empty_input(void) +{ + int rc; + bool match; + + rc = mosquitto_sub_matches_acl_with_pattern(NULL, NULL, NULL, NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("acl", NULL, NULL, NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern(NULL, "sub", NULL, NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern(NULL, NULL, "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern(NULL, NULL, NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("acl", "", "", "", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("", "sub", "", "", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("", "", "clientid", "", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("", "", "", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%c", "sub", NULL, NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%u", "sub", NULL, NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%c", "", "", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%u", "", NULL, "", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/test", "test//test", "", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%u/test", "test//test", NULL, "", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); +} + static void TEST_sub_match_empty_input(void) { int rc; @@ -278,10 +344,10 @@ static void TEST_invalid(void) } /* ======================================================================== - * PATTERNS + * TOPIC MATCHES SUB PATTERNS * ======================================================================== */ -static void TEST_pattern_clientid(void) +static void TEST_topic_pattern_clientid(void) { int rc; bool match; @@ -337,7 +403,7 @@ static void TEST_pattern_clientid(void) CU_ASSERT_EQUAL(match, false); } -static void TEST_pattern_username(void) +static void TEST_topic_pattern_username(void) { int rc; bool match; @@ -393,7 +459,7 @@ static void TEST_pattern_username(void) CU_ASSERT_EQUAL(match, false); } -static void TEST_pattern_both(void) +static void TEST_topic_pattern_both(void) { int rc; bool match; @@ -448,7 +514,7 @@ static void TEST_pattern_both(void) CU_ASSERT_EQUAL(match, false); } -static void TEST_pattern_wildcard(void) +static void TEST_topic_pattern_wildcard(void) { int rc; bool match; @@ -530,6 +596,430 @@ static void TEST_pattern_wildcard(void) CU_ASSERT_EQUAL(match, false); } +/* ======================================================================== + * SUB MATCHES ACL PATTERNS + * ======================================================================== */ + +static void TEST_acl_pattern_clientid(void) +{ + int rc; + bool match; + + /* Sole pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("%c", "clientid", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%c", "clientid", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern at beginning */ + rc = mosquitto_sub_matches_acl_with_pattern("%c/test", "clientid/test", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%c/test", "clientid/test", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern at end */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%c", "test/clientid", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%c", "test/clientid", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern in middle */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/test", "test/clientid/test", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/test", "test/clientid/test", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Repeated pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/%c/test", "test/clientid/clientid/test", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/%c/test", "test/clientid/clientid/test", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Not a pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%count", "test/clientid", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Now repeated, with wildcards: */ + + /* Pattern at beginning */ + rc = mosquitto_sub_matches_acl_with_pattern("%c/test/+", "clientid/test/+", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%c/test/+", "clientid/test/+", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%c/test/#", "clientid/test/+", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%c/test/#", "clientid/test/+", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern at end */ + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%c", "+/test/clientid", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%c", "+/test/clientid", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern in middle */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/+/test", "test/clientid/+/test", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/+/test", "test/clientid/+/test", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Repeated pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/%c/test/+", "test/clientid/clientid/test/test", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%c/%c/test/+", "test/clientid/clientid/test/test", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Not a pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%count", "+/test/clientid", "clientid", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); +} + +static void TEST_acl_pattern_username(void) +{ + int rc; + bool match; + + /* Sole pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("%u", "username", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%u", "username", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern at beginning */ + rc = mosquitto_sub_matches_acl_with_pattern("%u/test", "username/test", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%u/test", "username/test", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern at end */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%u", "test/username", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%u", "test/username", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern in middle */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%u/test", "test/username/test", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%u/test", "test/username/test", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Repeated pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%u/%u/test", "test/username/username/test", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("test/%u/%u/test", "test/username/username/test", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Not a pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%username", "test/username", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Now repeat with wildcards: */ + + /* Pattern at beginning */ + rc = mosquitto_sub_matches_acl_with_pattern("%u/test/+", "username/test/+", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%u/test/+", "username/test/+", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%u/#", "username/test/+", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%u/#", "username/test/+", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern at end */ + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%u", "+/test/username", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%u", "+/test/username", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern in middle */ + rc = mosquitto_sub_matches_acl_with_pattern("+/%u/test", "test/username/test", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/%u/test", "test/username/test", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Repeated pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%u/%u/test", "+/test/username/username/test", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%u/%u/test", "+/test/username/username/test", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Not a pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%username/+", "+/test/username/+", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); +} + +static void TEST_acl_pattern_both(void) +{ + int rc; + bool match; + + /* Sole pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("%u/%c", "username/clientid", "clientid", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("%u/%c", "username/clientid", "clientid", "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%u/%c", "username/clientid", "nomatch", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%u/%c", "username/clientid", "nomatch", "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Pattern in middle */ + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%c/%u/#", "+/test/clientid/username/test", "clientid", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%c/%u/#", "+/test/clientid/username/test", "clientid", "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%c/%u/test", "a/test/clientid/username/test", "nomatch", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("+/test/%c/%u/test", "a/test/clientid/username/test", "nomatch", "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Repeated pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%u/%c/%c/%u/#", "test/username/clientid/clientid/username/#", "clientid", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + /* Not a pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/%username/+/%client", "test/username/a/clientid", "clientid", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Not a pattern */ + rc = mosquitto_sub_matches_acl_with_pattern("test/a%u/+/a%c", "test/ausername/a/aclientid", "clientid", "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); +} + +static void TEST_acl_pattern_wildcard(void) +{ + int rc; + bool match; + + /* Malicious */ + /* ========= */ + + /* / in client id */ + rc = mosquitto_sub_matches_acl_with_pattern("%c", "clientid/test", "clientid/test", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%c", "/", "/", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* / in username */ + rc = mosquitto_sub_matches_acl_with_pattern("%u", "username/test", NULL, "username/test", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%u", "/", NULL, "/", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* + in client id */ + rc = mosquitto_sub_matches_acl_with_pattern("%c", "clientid", "+", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("%c", "+", "+", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* + in username */ + rc = mosquitto_sub_matches_acl_with_pattern("username/%u/+", "username/test/+", NULL, "+", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("username/%u/+", "username/+/+", NULL, "+", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("username/%u/+", "username/+", NULL, "+/a", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* # in client id */ + rc = mosquitto_sub_matches_acl_with_pattern("%c", "#", "#", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* # in username */ + rc = mosquitto_sub_matches_acl_with_pattern("%u", "#", NULL, "#", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Valid */ + /* ========= */ + + /* Ends in + */ + rc = mosquitto_sub_matches_acl_with_pattern("clientid/%c/+", "clientid/test/topic", "test", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("clientid/%c/+", "clientid/test/topic", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("username/%u/+", "username/test/topic", NULL, "test", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("username/%u/+", "username/test/topic", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + /* Ends in # */ + rc = mosquitto_sub_matches_acl_with_pattern("+/clientid/%c/#", "+/clientid/test/topic", "test", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/clientid/%c/#", "+/clientid/test/topic", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("+/username/%u/#", "+/username/test/topic", NULL, "test", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/username/%u/#", "+/username/test/topic", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("+/clientid/%c/#", "+/clientid/test", "test", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/clientid/%c/#", "+/clientid/test", "nomatch", NULL, &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl_with_pattern("+/pattern/%u/#", "+/pattern/username", NULL, "username", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl_with_pattern("+/username/%u/#", "+/username/test", NULL, "nomatch", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); +} + + +static void TEST_acl_pattern_wildcard_wildcard(void) +{ + int rc; + bool match; + + rc = mosquitto_sub_matches_acl("$SYS/#", "$SYS/broker/#", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl("$SYS/#", "$SYS/+/#", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl("$SYS/#", "$SYS", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, true); + + rc = mosquitto_sub_matches_acl("$SYS/+", "$SYS/#", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl("$SYS/+/a", "$SYS/a/+", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl("$SYS/+", "$SYS/+/a", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl("$SYS/broker/uptime", "$SYS/broker/#", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); + + rc = mosquitto_sub_matches_acl("#", "$SYS/broker/#", &match); + CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); + CU_ASSERT_EQUAL(match, false); +} + /* ======================================================================== * PUB TOPIC CHECK * ======================================================================== */ @@ -680,11 +1170,17 @@ int init_util_topic_tests(void) || !CU_add_test(test_suite, "Pub topic: Invalid", TEST_pub_topic_invalid) || !CU_add_test(test_suite, "Sub topic: Valid", TEST_sub_topic_valid) || !CU_add_test(test_suite, "Sub topic: Invalid", TEST_sub_topic_invalid) - || !CU_add_test(test_suite, "Pattern topic: Empty input", TEST_pattern_empty_input) - || !CU_add_test(test_suite, "Pattern topic: clientid", TEST_pattern_clientid) - || !CU_add_test(test_suite, "Pattern topic: username", TEST_pattern_username) - || !CU_add_test(test_suite, "Pattern topic: both", TEST_pattern_both) - || !CU_add_test(test_suite, "Pattern topic: wildcard", TEST_pattern_wildcard) + || !CU_add_test(test_suite, "Pattern topic: Empty input", TEST_topic_pattern_empty_input) + || !CU_add_test(test_suite, "Pattern topic: clientid", TEST_topic_pattern_clientid) + || !CU_add_test(test_suite, "Pattern topic: username", TEST_topic_pattern_username) + || !CU_add_test(test_suite, "Pattern topic: both", TEST_topic_pattern_both) + || !CU_add_test(test_suite, "Pattern topic: wildcard", TEST_topic_pattern_wildcard) + || !CU_add_test(test_suite, "Pattern acl: Empty input", TEST_acl_pattern_empty_input) + || !CU_add_test(test_suite, "Pattern acl: clientid", TEST_acl_pattern_clientid) + || !CU_add_test(test_suite, "Pattern acl: username", TEST_acl_pattern_username) + || !CU_add_test(test_suite, "Pattern acl: both", TEST_acl_pattern_both) + || !CU_add_test(test_suite, "Pattern acl: wildcard", TEST_acl_pattern_wildcard) + || !CU_add_test(test_suite, "Pattern acl: wildcard vs wildcard", TEST_acl_pattern_wildcard_wildcard) || !CU_add_test(test_suite, "Sub matching: Empty input", TEST_sub_match_empty_input) || !CU_add_test(test_suite, "Sub matching: normal", TEST_sub_match_acl) ){ @@ -695,5 +1191,3 @@ int init_util_topic_tests(void) return 0; } -#if 0 -#endif