ubusd_acl: rework wildcard support
Wildcard access list support was failing in case multiple wildcards entries were defined and/or when a specific access list string overlapped a wildcard entry. Root cause of the problem was the way how wildcard entries were sorted in the avl tree by the compare function ubusd_acl_match_path resulting into a non acces list match for a given object path. The avl_tree sorting has been changed to make use of avl_strcmp; as such there's no distinction anymore between non-wildcard and wildcard entries in the avl_tree compare function as the boolean partial marks an access list entry as a wildcard entry. When trying to find an access list match for an object path the access list tree is iterated as long as the number of characters between the access list string and object path is monotonically increasing. Signed-off-by: Hans Dedecker <dedeckeh@gmail.com>
This commit is contained in:
parent
73bd84748f
commit
c035bab01c
1 changed files with 69 additions and 42 deletions
109
ubusd_acl.c
109
ubusd_acl.c
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
* Copyright (C) 2015 John Crispin <blogic@openwrt.org>
|
||||||
|
* Copyright (C) 2018 Hans Dedecker <dedeckeh@gmail.com>
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU Lesser General Public License version 2.1
|
* it under the terms of the GNU Lesser General Public License version 2.1
|
||||||
|
@ -40,6 +41,8 @@ struct ubusd_acl_obj {
|
||||||
struct avl_node avl;
|
struct avl_node avl;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
|
||||||
|
bool partial;
|
||||||
|
|
||||||
const char *user;
|
const char *user;
|
||||||
const char *group;
|
const char *group;
|
||||||
|
|
||||||
|
@ -68,19 +71,6 @@ static struct avl_tree ubusd_acls;
|
||||||
static int ubusd_acl_seq;
|
static int ubusd_acl_seq;
|
||||||
static struct ubus_object *acl_obj;
|
static struct ubus_object *acl_obj;
|
||||||
|
|
||||||
static int
|
|
||||||
ubusd_acl_match_path(const void *k1, const void *k2, void *ptr)
|
|
||||||
{
|
|
||||||
const char *name = k1;
|
|
||||||
const char *match = k2;
|
|
||||||
char *wildcard = strstr(match, "\t");
|
|
||||||
|
|
||||||
if (wildcard)
|
|
||||||
return strncmp(name, match, wildcard - match);
|
|
||||||
|
|
||||||
return strcmp(name, match);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj)
|
ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj)
|
||||||
{
|
{
|
||||||
|
@ -98,22 +88,36 @@ ubusd_acl_check(struct ubus_client *cl, const char *obj,
|
||||||
const char *method, enum ubusd_acl_type type)
|
const char *method, enum ubusd_acl_type type)
|
||||||
{
|
{
|
||||||
struct ubusd_acl_obj *acl;
|
struct ubusd_acl_obj *acl;
|
||||||
struct blob_attr *cur;
|
int match_len = 0;
|
||||||
int rem;
|
|
||||||
|
|
||||||
if (!cl->uid || !obj)
|
if (!cl || !cl->uid || !obj)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
acl = avl_find_ge_element(&ubusd_acls, obj, acl, avl);
|
/*
|
||||||
if (!acl)
|
* Since this tree is sorted alphabetically, we can only expect
|
||||||
return -1;
|
* to find matching entries as long as the number of matching
|
||||||
|
* characters between the access list string and the object path
|
||||||
|
* is monotonically increasing.
|
||||||
|
*/
|
||||||
|
avl_for_each_element(&ubusd_acls, acl, avl) {
|
||||||
|
const char *key = acl->avl.key;
|
||||||
|
int cur_match_len;
|
||||||
|
bool full_match;
|
||||||
|
|
||||||
avl_for_element_to_last(&ubusd_acls, acl, acl, avl) {
|
full_match = ubus_strmatch_len(obj, key, &cur_match_len);
|
||||||
int diff = ubusd_acl_match_path(obj, acl->avl.key, NULL);
|
if (cur_match_len < match_len)
|
||||||
|
|
||||||
if (diff)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
match_len = cur_match_len;
|
||||||
|
|
||||||
|
if (!full_match) {
|
||||||
|
if (!acl->partial)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (match_len != strlen(key))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (ubusd_acl_match_cred(cl, acl))
|
if (ubusd_acl_match_cred(cl, acl))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@ -129,11 +133,15 @@ ubusd_acl_check(struct ubus_client *cl, const char *obj,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UBUS_ACL_ACCESS:
|
case UBUS_ACL_ACCESS:
|
||||||
if (acl->methods)
|
if (acl->methods) {
|
||||||
|
struct blob_attr *cur;
|
||||||
|
int rem;
|
||||||
|
|
||||||
blobmsg_for_each_attr(cur, acl->methods, rem)
|
blobmsg_for_each_attr(cur, acl->methods, rem)
|
||||||
if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
|
if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
|
||||||
if (!ubusd_acl_match_path(method, blobmsg_get_string(cur), NULL))
|
if (!strcmp(method, blobmsg_get_string(cur)))
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -212,19 +220,20 @@ static struct ubusd_acl_obj*
|
||||||
ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
|
ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
|
||||||
{
|
{
|
||||||
struct ubusd_acl_obj *o;
|
struct ubusd_acl_obj *o;
|
||||||
|
int len = strlen(obj);
|
||||||
char *k;
|
char *k;
|
||||||
|
bool partial = false;
|
||||||
|
|
||||||
o = calloc_a(sizeof(*o), &k, strlen(obj) + 1);
|
if (obj[len - 1] == '*') {
|
||||||
|
partial = true;
|
||||||
|
len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
o = calloc_a(sizeof(*o), &k, len + 1);
|
||||||
|
o->partial = partial;
|
||||||
o->user = file->user;
|
o->user = file->user;
|
||||||
o->group = file->group;
|
o->group = file->group;
|
||||||
o->avl.key = k;
|
o->avl.key = memcpy(k, obj, len);
|
||||||
strcpy(k, obj);
|
|
||||||
|
|
||||||
while (*k) {
|
|
||||||
if (*k == '*')
|
|
||||||
*k = '\t';
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
|
|
||||||
list_add(&o->list, &file->acl);
|
list_add(&o->list, &file->acl);
|
||||||
avl_insert(&ubusd_acls, &o->avl);
|
avl_insert(&ubusd_acls, &o->avl);
|
||||||
|
@ -420,23 +429,40 @@ static void
|
||||||
ubusd_reply_add(struct ubus_object *obj)
|
ubusd_reply_add(struct ubus_object *obj)
|
||||||
{
|
{
|
||||||
struct ubusd_acl_obj *acl;
|
struct ubusd_acl_obj *acl;
|
||||||
|
int match_len = 0;
|
||||||
|
|
||||||
if (!obj->path.key)
|
if (!obj->path.key)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
acl = avl_find_ge_element(&ubusd_acls, obj->path.key, acl, avl);
|
/*
|
||||||
if (!acl)
|
* Since this tree is sorted alphabetically, we can only expect
|
||||||
return;
|
* to find matching entries as long as the number of matching
|
||||||
|
* characters between the access list string and the object path
|
||||||
avl_for_element_to_last(&ubusd_acls, acl, acl, avl) {
|
* is monotonically increasing.
|
||||||
|
*/
|
||||||
|
avl_for_each_element(&ubusd_acls, acl, avl) {
|
||||||
|
const char *key = acl->avl.key;
|
||||||
|
int cur_match_len;
|
||||||
|
bool full_match;
|
||||||
void *c;
|
void *c;
|
||||||
|
|
||||||
if (!acl->priv)
|
if (!acl->priv)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (ubusd_acl_match_path(obj->path.key, acl->avl.key, NULL))
|
full_match = ubus_strmatch_len(obj->path.key, key, &cur_match_len);
|
||||||
|
if (cur_match_len < match_len)
|
||||||
|
break;
|
||||||
|
|
||||||
|
match_len = cur_match_len;
|
||||||
|
|
||||||
|
if (!full_match) {
|
||||||
|
if (!acl->partial)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (match_len != strlen(key))
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
c = blobmsg_open_table(&b, NULL);
|
c = blobmsg_open_table(&b, NULL);
|
||||||
blobmsg_add_string(&b, "obj", obj->path.key);
|
blobmsg_add_string(&b, "obj", obj->path.key);
|
||||||
if (acl->user)
|
if (acl->user)
|
||||||
|
@ -450,6 +476,7 @@ ubusd_reply_add(struct ubus_object *obj)
|
||||||
blobmsg_close_table(&b, c);
|
blobmsg_close_table(&b, c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr, struct blob_attr *msg)
|
static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr, struct blob_attr *msg)
|
||||||
{
|
{
|
||||||
struct ubus_object *obj;
|
struct ubus_object *obj;
|
||||||
|
@ -489,7 +516,7 @@ static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const
|
||||||
|
|
||||||
void ubusd_acl_init(void)
|
void ubusd_acl_init(void)
|
||||||
{
|
{
|
||||||
avl_init(&ubusd_acls, ubusd_acl_match_path, true, NULL);
|
ubus_init_string_tree(&ubusd_acls, true);
|
||||||
acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL);
|
acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL);
|
||||||
acl_obj->recv_msg = ubusd_acl_recv;
|
acl_obj->recv_msg = ubusd_acl_recv;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue