219 lines
4.4 KiB
C
219 lines
4.4 KiB
C
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static char *strtok_hier(char *str, char **saveptr)
|
|
{
|
|
char *c;
|
|
|
|
if(str != NULL){
|
|
*saveptr = str;
|
|
}
|
|
|
|
if(*saveptr == NULL){
|
|
return NULL;
|
|
}
|
|
|
|
c = strchr(*saveptr, '/');
|
|
if(c){
|
|
str = *saveptr;
|
|
*saveptr = c+1;
|
|
c[0] = '\0';
|
|
}else if(*saveptr){
|
|
/* No match, but surplus string */
|
|
str = *saveptr;
|
|
*saveptr = NULL;
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
static int count_hier_levels(const char *s)
|
|
{
|
|
int count = 1;
|
|
const char *c = s;
|
|
|
|
while((c = strchr(c, '/')) && c[0]){
|
|
c++;
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
|
|
static bool hash_check(char *s, size_t *len)
|
|
{
|
|
if((*len) == 1 && s[0] == '#'){
|
|
s[0] = '\0';
|
|
(*len)--;
|
|
return true;
|
|
}else if((*len) > 1 && s[(*len)-2] == '/' && s[(*len)-1] == '#'){
|
|
s[(*len)-2] = '\0';
|
|
s[(*len)-1] = '\0';
|
|
(*len) -= 2;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool sub_acl_check(const char *acl, const char *sub)
|
|
{
|
|
char *acl_local;
|
|
char *sub_local;
|
|
size_t acl_len, sub_len;
|
|
bool acl_hash = false, sub_hash = false;
|
|
int acl_levels, sub_levels;
|
|
int i;
|
|
char *acl_token, *sub_token;
|
|
char *acl_saveptr, *sub_saveptr;
|
|
|
|
acl_len = strlen(acl);
|
|
if(acl_len == 1 && acl[0] == '#'){
|
|
return true;
|
|
}
|
|
|
|
sub_len = strlen(sub);
|
|
//mosquitto_validate_utf8(acl, acl_len);
|
|
|
|
acl_local = strdup(acl);
|
|
sub_local = strdup(sub);
|
|
if(acl_local == NULL || sub_local == NULL){
|
|
free(acl_local);
|
|
free(sub_local);
|
|
return false;
|
|
}
|
|
|
|
acl_hash = hash_check(acl_local, &acl_len);
|
|
sub_hash = hash_check(sub_local, &sub_len);
|
|
|
|
if(sub_hash == true && acl_hash == false){
|
|
free(acl_local);
|
|
free(sub_local);
|
|
return false;
|
|
}
|
|
|
|
acl_levels = count_hier_levels(acl_local);
|
|
sub_levels = count_hier_levels(sub_local);
|
|
if(acl_levels > sub_levels){
|
|
free(acl_local);
|
|
free(sub_local);
|
|
return false;
|
|
}else if(sub_levels > acl_levels){
|
|
if(acl_hash == false){
|
|
free(acl_local);
|
|
free(sub_local);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
acl_saveptr = acl_local;
|
|
sub_saveptr = sub_local;
|
|
for(i=0; i<sub_levels; i++){
|
|
acl_token = strtok_hier(acl_saveptr, &acl_saveptr);
|
|
sub_token = strtok_hier(sub_saveptr, &sub_saveptr);
|
|
|
|
if(i<acl_levels &&
|
|
(!strcmp(acl_token, "+")
|
|
|| !strcmp(acl_token, sub_token))){
|
|
|
|
/* This level matches a single level wildcard, or is an exact
|
|
* match, so carry on checking. */
|
|
}else if(i>=acl_levels && acl_hash == true){
|
|
/* The sub has more levels of hierarchy than the acl, but the acl
|
|
* ends in a multi level wildcard so the match is fine. */
|
|
}else{
|
|
free(acl_local);
|
|
free(sub_local);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
free(acl_local);
|
|
free(sub_local);
|
|
return true;
|
|
}
|
|
|
|
|
|
#ifdef TEST
|
|
|
|
#define BLK "\e[0;30m"
|
|
#define RED "\e[0;31m"
|
|
#define GRN "\e[0;32m"
|
|
#define YEL "\e[0;33m"
|
|
#define BLU "\e[0;34m"
|
|
#define MAG "\e[0;35m"
|
|
#define CYN "\e[0;36m"
|
|
#define WHT "\e[0;37m"
|
|
#define RST "\e[0m"
|
|
|
|
void hier_test(const char *s, int expected)
|
|
{
|
|
int levels;
|
|
|
|
levels = count_hier_levels(s);
|
|
printf("HIER %s %d:%d ", s, expected, levels);
|
|
if(levels == expected){
|
|
printf(GRN "passed" RST "\n");
|
|
}else{
|
|
printf(RED "failed" RST "\n");
|
|
}
|
|
}
|
|
|
|
void test(const char *sub1, const char *sub2, bool expected)
|
|
{
|
|
bool result;
|
|
|
|
printf("ACL %s : %s ", sub1, sub2);
|
|
result = sub_acl_check(sub1, sub2);
|
|
if(result == expected){
|
|
printf(GRN "passed\n" RST);
|
|
}else{
|
|
printf(RED "failed\n" RST);
|
|
}
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
hier_test("foo/+/bar", 3);
|
|
hier_test("foo/#", 2);
|
|
hier_test("foo/+/ba℞/#", 4);
|
|
hier_test("foo/baz/ba℞", 3);
|
|
hier_test("foo/+/ba℞/#", 4);
|
|
hier_test("foo/baz/ba℞/+", 4);
|
|
hier_test("foo/+/ba℞/#", 4);
|
|
hier_test("foo/baz/ba℞/#", 4);
|
|
hier_test("foo/+/ba℞/#", 4);
|
|
hier_test("foo/baz/+/#", 4);
|
|
hier_test("/+//#", 4);
|
|
hier_test("/foo///#", 5);
|
|
hier_test("#", 1);
|
|
hier_test("+", 1);
|
|
hier_test("/", 2);
|
|
hier_test("////////////////////////////////////////////////////////////////////////////////////////////////////", 101);
|
|
|
|
test("foo/+/bar", "foo/#", false);
|
|
test("foo/+/ba℞/#", "foo/baz/ba℞", true);
|
|
test("foo/+/ba℞/#", "foo/baz/ba℞/+", true);
|
|
test("foo/+/ba℞/#", "foo/baz/ba℞/#", true);
|
|
test("foo/+/ba℞/#", "foo/baz/+/#", false);
|
|
test("/+//#", "/foo///#", true);
|
|
test("#", "#", true);
|
|
test("#", "+", true);
|
|
test("/#", "+", false);
|
|
test("/#", "/+", true);
|
|
test("/+", "#", false);
|
|
test("/+", "+", false);
|
|
test("+/+", "topic/topic", true);
|
|
test("+/+", "topic/topic/", false);
|
|
test("+", "#", false);
|
|
test("+", "+", true);
|
|
test("a/b/c/d/e", "a/b/c/d/e", true);
|
|
test("a/b/ /d/e", "a/b/c/d/e", false);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|