examples: remove dead increments
[project/ubus.git] / ubusd_acl.c
index 491b233427abab7cfa1b4be495b7b2a77990e0b6..f19df9a875c7671257d5c3d4a582edb5371f42a6 100644 (file)
@@ -1,5 +1,6 @@
 /*
  * 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
  * 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 list_head list;
 
+       bool partial;
+
        const char *user;
        const char *group;
 
@@ -48,6 +51,8 @@ struct ubusd_acl_obj {
        struct blob_attr *priv;
        bool subscribe;
        bool publish;
+       bool listen;
+       bool send;
 };
 
 struct ubusd_acl_file {
@@ -62,24 +67,12 @@ struct ubusd_acl_file {
        int ok;
 };
 
+const char *ubusd_acl_dir = "/usr/share/acl.d";
 static struct blob_buf bbuf;
 static struct avl_tree ubusd_acls;
 static int ubusd_acl_seq;
 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
 ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj)
 {
@@ -97,24 +90,39 @@ ubusd_acl_check(struct ubus_client *cl, const char *obj,
                const char *method, enum ubusd_acl_type type)
 {
        struct ubusd_acl_obj *acl;
-       struct blob_attr *cur;
-       int rem;
+       int match_len = 0;
 
-       if (!cl->gid && !cl->uid)
+       if (!cl || !cl->uid || !obj)
                return 0;
 
-       acl = avl_find_ge_element(&ubusd_acls, obj, acl, avl);
-       while (acl) {
-               int diff = ubusd_acl_match_path(obj, acl->avl.key, NULL);
-
-               if (diff)
+       /*
+        * Since this tree is sorted alphabetically, we can only expect
+        * 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;
+
+               full_match = ubus_strmatch_len(obj, key, &cur_match_len);
+               if (cur_match_len < match_len)
                        break;
 
-               if (ubusd_acl_match_cred(cl, acl)) {
-                       acl = avl_next_element(acl, avl);
-                       continue;
+               match_len = cur_match_len;
+
+               if (!full_match) {
+                       if (!acl->partial)
+                               continue;
+
+                       if (match_len != (int) strlen(key))
+                               continue;
                }
 
+               if (ubusd_acl_match_cred(cl, acl))
+                       continue;
+
                switch (type) {
                case UBUS_ACL_PUBLISH:
                        if (acl->publish)
@@ -126,15 +134,28 @@ ubusd_acl_check(struct ubus_client *cl, const char *obj,
                                return 0;
                        break;
 
+               case UBUS_ACL_LISTEN:
+                       if (acl->listen)
+                               return 0;
+                       break;
+
+               case UBUS_ACL_SEND:
+                       if (acl->send)
+                               return 0;
+                       break;
+
                case UBUS_ACL_ACCESS:
-                       if (acl->methods)
+                       if (acl->methods) {
+                               struct blob_attr *cur;
+                               size_t rem;
+
                                blobmsg_for_each_attr(cur, acl->methods, rem)
                                        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;
+                       }
                        break;
                }
-               acl = avl_next_element(acl, avl);
        }
 
        return -1;
@@ -173,6 +194,13 @@ ubusd_acl_init_client(struct ubus_client *cl, int fd)
        return 0;
 }
 
+void
+ubusd_acl_free_client(struct ubus_client *cl)
+{
+       free(cl->group);
+       free(cl->user);
+}
+
 static void
 ubusd_acl_file_free(struct ubusd_acl_file *file)
 {
@@ -204,19 +232,20 @@ static struct ubusd_acl_obj*
 ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
 {
        struct ubusd_acl_obj *o;
+       int len = strlen(obj);
        char *k;
+       bool partial = false;
+
+       if (obj[len - 1] == '*') {
+               partial = true;
+               len--;
+       }
 
-       o = calloc_a(sizeof(*o), &k, strlen(obj) + 1);
+       o = calloc_a(sizeof(*o), &k, len + 1);
+       o->partial = partial;
        o->user = file->user;
        o->group = file->group;
-       o->avl.key = k;
-       strcpy(k, obj);
-
-       while (*k) {
-               if (*k == '*')
-                       *k = '\t';
-               k++;
-       }
+       o->avl.key = memcpy(k, obj, len);
 
        list_add(&o->list, &file->acl);
        avl_insert(&ubusd_acls, &o->avl);
@@ -262,6 +291,20 @@ ubusd_acl_add_publish(struct ubusd_acl_file *file, const char *obj)
        o->publish = true;
 }
 
+static void ubusd_acl_add_listen(struct ubusd_acl_file *file, const char *obj)
+{
+       struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
+
+       o->listen = true;
+}
+
+static void ubusd_acl_add_send(struct ubusd_acl_file *file, const char *obj)
+{
+       struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
+
+       o->send = true;
+}
+
 enum {
        ACL_USER,
        ACL_GROUP,
@@ -269,6 +312,8 @@ enum {
        ACL_PUBLISH,
        ACL_SUBSCRIBE,
        ACL_INHERIT,
+       ACL_LISTEN,
+       ACL_SEND,
        __ACL_MAX
 };
 
@@ -279,13 +324,15 @@ static const struct blobmsg_policy acl_policy[__ACL_MAX] = {
        [ACL_PUBLISH] = { .name = "publish", .type = BLOBMSG_TYPE_ARRAY },
        [ACL_SUBSCRIBE] = { .name = "subscribe", .type = BLOBMSG_TYPE_ARRAY },
        [ACL_INHERIT] = { .name = "inherit", .type = BLOBMSG_TYPE_ARRAY },
+       [ACL_LISTEN] = { .name= "listen", .type = BLOBMSG_TYPE_ARRAY },
+       [ACL_SEND] = { .name= "send", .type = BLOBMSG_TYPE_ARRAY },
 };
 
 static void
 ubusd_acl_file_add(struct ubusd_acl_file *file)
 {
        struct blob_attr *tb[__ACL_MAX], *cur;
-       int rem;
+       size_t rem;
 
        blobmsg_parse(acl_policy, __ACL_MAX, tb, blob_data(file->blob),
                      blob_len(file->blob));
@@ -297,9 +344,6 @@ ubusd_acl_file_add(struct ubusd_acl_file *file)
        else
                return;
 
-       if (!tb[ACL_ACCESS] && !tb[ACL_PUBLISH] && !tb[ACL_INHERIT])
-               return;
-
        if (tb[ACL_ACCESS])
                blobmsg_for_each_attr(cur, tb[ACL_ACCESS], rem)
                        ubusd_acl_add_access(file, cur);
@@ -313,6 +357,16 @@ ubusd_acl_file_add(struct ubusd_acl_file *file)
                blobmsg_for_each_attr(cur, tb[ACL_PUBLISH], rem)
                        if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
                                ubusd_acl_add_publish(file, blobmsg_get_string(cur));
+
+       if (tb[ACL_LISTEN])
+               blobmsg_for_each_attr(cur, tb[ACL_LISTEN], rem)
+                       if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
+                               ubusd_acl_add_listen(file, blobmsg_get_string(cur));
+
+       if (tb[ACL_SEND])
+               blobmsg_for_each_attr(cur, tb[ACL_SEND], rem)
+                       if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
+                               ubusd_acl_add_send(file, blobmsg_get_string(cur));
 }
 
 static void
@@ -381,9 +435,12 @@ ubusd_acl_load(void)
 {
        struct stat st;
        glob_t gl;
-       int j;
+       size_t j;
+       const char *suffix = "/*.json";
+       char *path = alloca(strlen(ubusd_acl_dir) + strlen(suffix) + 1);
 
-       if (glob("/usr/share/acl.d/*.json", GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
+       sprintf(path, "%s%s", ubusd_acl_dir, suffix);
+       if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
                return;
 
        vlist_update(&ubusd_acl_files);
@@ -412,31 +469,54 @@ static void
 ubusd_reply_add(struct ubus_object *obj)
 {
        struct ubusd_acl_obj *acl;
+       int match_len = 0;
 
        if (!obj->path.key)
                return;
-       acl = avl_find_ge_element(&ubusd_acls, obj->path.key, acl, avl);
-       while (acl && !avl_is_last(&ubusd_acls, &acl->avl) &&
-                     !ubusd_acl_match_path(obj->path.key, acl->avl.key, NULL)) {
 
-               if (acl->priv) {
-                       void *c = blobmsg_open_table(&b, NULL);
+       /*
+        * Since this tree is sorted alphabetically, we can only expect
+        * 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;
+               void *c;
+
+               if (!acl->priv)
+                       continue;
+
+               full_match = ubus_strmatch_len(obj->path.key, key, &cur_match_len);
+               if (cur_match_len < match_len)
+                       break;
 
-                       blobmsg_add_string(&b, "obj", obj->path.key);
-                       if (acl->user)
-                               blobmsg_add_string(&b, "user", acl->user);
-                       if (acl->group)
-                               blobmsg_add_string(&b, "group", acl->group);
+               match_len = cur_match_len;
 
-                       if (acl->priv)
-                               blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl",
-                                       blobmsg_data(acl->priv), blobmsg_data_len(acl->priv));
+               if (!full_match) {
+                       if (!acl->partial)
+                               continue;
 
-                       blobmsg_close_table(&b, c);
+                       if (match_len != (int) strlen(key))
+                               continue;
                }
-               acl = avl_next_element(acl, avl);
+
+               c = blobmsg_open_table(&b, NULL);
+               blobmsg_add_string(&b, "obj", obj->path.key);
+               if (acl->user)
+                       blobmsg_add_string(&b, "user", acl->user);
+               if (acl->group)
+                       blobmsg_add_string(&b, "group", acl->group);
+
+               blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl",
+                       blobmsg_data(acl->priv), blobmsg_data_len(acl->priv));
+
+               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)
 {
        struct ubus_object *obj;
@@ -476,7 +556,7 @@ static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const
 
 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->recv_msg = ubusd_acl_recv;
 }