Browse Source

add acl code

Signed-off-by: John Crispin <blogic@openwrt.org>
John Crispin 9 years ago
parent
commit
8309c75828
7 changed files with 687 additions and 3 deletions
  1. 3 3
      CMakeLists.txt
  2. 153 0
      libubus-acl.c
  3. 24 0
      libubus.h
  4. 7 0
      ubusd.h
  5. 469 0
      ubusd_acl.c
  6. 27 0
      ubusd_acl.h
  7. 4 0
      ubusmsg.h

+ 3 - 3
CMakeLists.txt

@@ -19,11 +19,11 @@ IF(APPLE)
   LINK_DIRECTORIES(/opt/local/lib)
 ENDIF()
 
-ADD_LIBRARY(ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c)
+ADD_LIBRARY(ubus SHARED libubus.c libubus-io.c libubus-obj.c libubus-sub.c libubus-req.c libubus-acl.c)
 TARGET_LINK_LIBRARIES(ubus ubox)
 
-ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c)
-TARGET_LINK_LIBRARIES(ubusd ubox)
+ADD_EXECUTABLE(ubusd ubusd.c ubusd_id.c ubusd_obj.c ubusd_proto.c ubusd_event.c ubusd_acl.c)
+TARGET_LINK_LIBRARIES(ubusd ubox blobmsg_json ${json})
 
 find_library(json NAMES json-c json)
 ADD_EXECUTABLE(cli cli.c)

+ 153 - 0
libubus-acl.c

@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2015 John Cripin <blogic@openwrt.org>
+ *
+ * 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
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <unistd.h>
+
+#include <libubox/blob.h>
+#include <libubox/blobmsg.h>
+
+#include "libubus.h"
+#include <libubox/avl-cmp.h>
+
+static struct ubus_event_handler acl_event;
+static struct ubus_request acl_req;
+static struct blob_attr *acl_blob;
+
+static int acl_cmp(const void *k1, const void *k2, void *ptr)
+{
+	const struct ubus_acl_key *key1 = k1;
+	const struct ubus_acl_key *key2 = k2;
+	int ret = 0;
+
+	if (key1->user && key2->user)
+		ret = strcmp(key1->user, key2->user);
+	if (ret)
+		return ret;
+
+	if (key1->group && key2->group)
+		ret = strcmp(key1->group, key2->group);
+	if (ret)
+		return ret;
+
+	return strcmp(key1->object, key2->object);
+}
+
+AVL_TREE(acl_objects, acl_cmp, true, NULL);
+
+enum {
+	ACL_OBJ_OBJECT,
+	ACL_OBJ_USER,
+	ACL_OBJ_GROUP,
+	ACL_OBJ_ACL,
+	__ACL_OBJ_MAX
+};
+
+static const struct blobmsg_policy acl_obj_policy[__ACL_OBJ_MAX] = {
+	[ACL_OBJ_OBJECT] = { .name = "obj", .type = BLOBMSG_TYPE_STRING },
+	[ACL_OBJ_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING },
+	[ACL_OBJ_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING },
+	[ACL_OBJ_ACL] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE },
+};
+
+static void
+acl_add(struct blob_attr *obj)
+{
+	struct blob_attr *tb[__ACL_OBJ_MAX];
+	struct acl_object *acl;
+
+	blobmsg_parse(acl_obj_policy, __ACL_OBJ_MAX, tb, blobmsg_data(obj),
+		      blobmsg_data_len(obj));
+
+	if (!tb[ACL_OBJ_OBJECT] || !tb[ACL_OBJ_ACL])
+		return;
+
+	if (!tb[ACL_OBJ_USER] && !tb[ACL_OBJ_GROUP])
+		return;
+
+	acl = calloc(1, sizeof(*acl));
+	if (!acl)
+		return;
+
+	acl->avl.key = &acl->key;
+	acl->key.object = blobmsg_get_string(tb[ACL_OBJ_OBJECT]);
+	acl->key.user = blobmsg_get_string(tb[ACL_OBJ_USER]);
+	acl->key.group = blobmsg_get_string(tb[ACL_OBJ_GROUP]);
+	acl->acl = tb[ACL_OBJ_ACL];
+	avl_insert(&acl_objects, &acl->avl);
+}
+
+enum {
+	ACL_POLICY_SEQ,
+	ACL_POLICY_ACL,
+	__ACL_POLICY_MAX
+};
+
+static const struct blobmsg_policy acl_policy[__ACL_POLICY_MAX] = {
+	[ACL_POLICY_SEQ] = { .name = "seq", .type = BLOBMSG_TYPE_INT32 },
+	[ACL_POLICY_ACL] = { .name = "acl", .type = BLOBMSG_TYPE_ARRAY },
+};
+
+static void acl_recv_cb(struct ubus_request *req,
+			int type, struct blob_attr *msg)
+{
+	struct blob_attr *tb[__ACL_POLICY_MAX];
+	struct blob_attr *cur;
+	int rem;
+
+	if (acl_blob) {
+		struct acl_object *p, *q;
+
+		avl_for_each_element_safe(&acl_objects, p, avl, q) {
+			avl_delete(&acl_objects, &p->avl);
+			free(p);
+		}
+		free(acl_blob);
+	}
+	acl_blob = blob_memdup(msg);
+	blobmsg_parse(acl_policy, __ACL_POLICY_MAX, tb, blobmsg_data(msg),
+		      blobmsg_data_len(msg));
+
+	if (!tb[ACL_POLICY_SEQ] && !tb[ACL_POLICY_ACL])
+		return;
+
+	blobmsg_for_each_attr(cur, tb[ACL_POLICY_ACL], rem)
+		acl_add(cur);
+}
+
+static void acl_query(struct ubus_context *ctx)
+{
+	ubus_invoke_async(ctx, UBUS_SYSTEM_OBJECT_ACL, "query", NULL, &acl_req);
+	acl_req.data_cb = acl_recv_cb;
+	ubus_complete_request_async(ctx, &acl_req);
+}
+
+static void acl_subscribe_cb(struct ubus_context *ctx, struct ubus_event_handler *ev,
+		const char *type, struct blob_attr *msg)
+{
+	if (strcmp(type, "ubus.acl.sequence"))
+		return;
+	acl_query(ctx);
+}
+
+int ubus_register_acl(struct ubus_context *ctx)
+{
+	int ret;
+
+	acl_event.cb = acl_subscribe_cb;
+
+	ret = ubus_register_event_handler(ctx, &acl_event, "ubus.acl.sequence");
+	if (!ret)
+		acl_query(ctx);
+
+	return ret;
+}

+ 24 - 0
libubus.h

@@ -171,11 +171,19 @@ struct ubus_object_data {
 	struct blob_attr *signature;
 };
 
+struct ubus_acl_key {
+	const char *user;
+	const char *group;
+	const char *object;
+};
+
 struct ubus_request_data {
 	uint32_t object;
 	uint32_t peer;
 	uint16_t seq;
 
+	struct ubus_acl_key acl;
+
 	/* internal use */
 	bool deferred;
 	int fd;
@@ -282,6 +290,22 @@ ubus_unregister_subscriber(struct ubus_context *ctx, struct ubus_subscriber *obj
 int ubus_subscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id);
 int ubus_unsubscribe(struct ubus_context *ctx, struct ubus_subscriber *obj, uint32_t id);
 
+
+/* ----------- acl ----------- */
+
+struct acl_object {
+	struct ubus_acl_key key;
+	struct avl_node avl;
+	struct blob_attr *acl;
+};
+
+extern struct avl_tree acl_objects;
+int ubus_register_acl(struct ubus_context *ctx);
+
+#define acl_for_each(o, m) \
+	if ((m)->object && (m)->user && (m)->group) \
+		avl_for_element_range(avl_find_ge_element(&acl_objects, m, o, avl), avl_find_le_element(&acl_objects, m, o, avl), o, avl)
+
 /* ----------- rpc ----------- */
 
 /* invoke a method on a specific object */

+ 7 - 0
ubusd.h

@@ -21,6 +21,7 @@
 #include "ubusd_id.h"
 #include "ubusd_obj.h"
 #include "ubusmsg.h"
+#include "ubusd_acl.h"
 
 #define UBUSD_CLIENT_BACKLOG	32
 #define UBUS_OBJ_HASH_BITS	4
@@ -39,6 +40,11 @@ struct ubus_client {
 	struct ubus_id id;
 	struct uloop_fd sock;
 
+	uid_t uid;
+	gid_t gid;
+	char *user;
+	char *group;
+
 	struct list_head objects;
 
 	struct ubus_msg_buf *tx_queue[UBUSD_CLIENT_BACKLOG];
@@ -76,5 +82,6 @@ void ubusd_send_obj_event(struct ubus_object *obj, bool add);
 int ubusd_send_event(struct ubus_client *cl, const char *id,
 		     event_fill_cb fill_cb, void *cb_priv);
 
+void ubusd_acl_init(void);
 
 #endif

+ 469 - 0
ubusd_acl.c

@@ -0,0 +1,469 @@
+/*
+ * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
+ *
+ * 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
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#define _GNU_SOURCE
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <syslog.h>
+#include <unistd.h>
+#include <glob.h>
+#include <grp.h>
+#include <pwd.h>
+
+#include <libubox/vlist.h>
+#include <libubox/blobmsg_json.h>
+#include <libubox/avl-cmp.h>
+
+#include "ubusd.h"
+
+struct ubusd_acl_obj {
+	struct avl_node avl;
+	struct list_head list;
+
+	const char *user;
+	const char *group;
+
+	struct blob_attr *methods;
+	struct blob_attr *tags;
+	struct blob_attr *priv;
+	bool subscribe;
+	bool publish;
+};
+
+struct ubusd_acl_file {
+	struct vlist_node avl;
+
+	const char *user;
+	const char *group;
+
+	struct blob_attr *blob;
+	struct list_head acl;
+
+	int ok;
+};
+
+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)
+{
+	if (obj->user && !strcmp(cl->user, obj->user))
+		return 0;
+
+	if (obj->group && !strcmp(cl->group, obj->group))
+		return 0;
+
+	return -1;
+}
+
+int
+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;
+
+	if (!cl->gid && !cl->gid)
+		return 0;
+
+	acl = avl_find_ge_element(&ubusd_acls, obj, acl, avl);
+	while (acl && !avl_is_last(&ubusd_acls, &acl->avl)) {
+		int diff = ubusd_acl_match_path(obj, acl->avl.key, NULL);
+
+		if (diff)
+			break;
+
+		if (ubusd_acl_match_cred(cl, acl)) {
+			acl = avl_next_element(acl, avl);
+			continue;
+		}
+
+		switch (type) {
+		case UBUS_ACL_PUBLISH:
+			if (acl->publish)
+				return 0;
+			break;
+
+		case UBUS_ACL_SUBSCRIBE:
+			if (acl->subscribe)
+				return 0;
+			break;
+
+		case UBUS_ACL_ACCESS:
+			if (acl->methods)
+				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))
+							return 0;
+			break;
+		}
+		acl = avl_next_element(acl, avl);
+	}
+
+	return -1;
+}
+
+int
+ubusd_acl_init_client(struct ubus_client *cl, int fd)
+{
+	unsigned int len = sizeof(struct ucred);
+	struct ucred cred;
+	struct passwd *pwd;
+	struct group *group;
+
+	if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
+		return -1;
+
+	pwd = getpwuid(cred.uid);
+	if (!pwd)
+		return -1;
+
+	group = getgrgid(cred.gid);
+	if (!group)
+		return -1;
+
+	cl->uid = cred.uid;
+	cl->gid = cred.gid;
+
+	cl->group = strdup(group->gr_name);
+	cl->user = strdup(pwd->pw_name);
+
+	return 0;
+}
+
+static void
+ubusd_acl_file_free(struct ubusd_acl_file *file)
+{
+	struct ubusd_acl_obj *p, *q;
+
+	list_for_each_entry_safe(p, q, &file->acl, list) {
+		avl_delete(&ubusd_acls, &p->avl);
+		list_del(&p->list);
+		free(p);
+	}
+
+	free(file);
+}
+
+enum {
+	ACL_ACCESS_METHODS,
+	ACL_ACCESS_TAGS,
+	ACL_ACCESS_PRIV,
+	__ACL_ACCESS_MAX
+};
+
+static const struct blobmsg_policy acl_obj_policy[__ACL_ACCESS_MAX] = {
+	[ACL_ACCESS_METHODS] = { .name = "methods", .type = BLOBMSG_TYPE_ARRAY },
+	[ACL_ACCESS_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY },
+	[ACL_ACCESS_PRIV] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE },
+};
+
+static struct ubusd_acl_obj*
+ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
+{
+	struct ubusd_acl_obj *o;
+	char *k;
+
+	o = calloc_a(1, sizeof(*o), &k, strlen(obj) + 1);
+	o->user = file->user;
+	o->group = file->group;
+	o->avl.key = k;
+	strcpy(k, obj);
+
+	while (*k) {
+		if (*k == '*')
+			*k = '\t';
+		k++;
+	}
+
+	list_add(&o->list, &file->acl);
+	avl_insert(&ubusd_acls, &o->avl);
+
+	return o;
+}
+
+static void
+ubusd_acl_add_access(struct ubusd_acl_file *file, struct blob_attr *obj)
+{
+	struct blob_attr *tb[__ACL_ACCESS_MAX];
+	struct ubusd_acl_obj *o;
+
+	blobmsg_parse(acl_obj_policy, __ACL_ACCESS_MAX, tb, blobmsg_data(obj),
+		      blobmsg_data_len(obj));
+
+	if (!tb[ACL_ACCESS_METHODS] && !tb[ACL_ACCESS_TAGS] && !tb[ACL_ACCESS_PRIV])
+		return;
+
+	o = ubusd_acl_alloc_obj(file, blobmsg_name(obj));
+
+	o->methods = tb[ACL_ACCESS_METHODS];
+	o->tags = tb[ACL_ACCESS_TAGS];
+	o->priv = tb[ACL_ACCESS_PRIV];
+
+	if (file->user || file->group)
+		file->ok = 1;
+}
+
+static void
+ubusd_acl_add_subscribe(struct ubusd_acl_file *file, const char *obj)
+{
+	struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
+
+	o->subscribe = true;
+}
+
+static void
+ubusd_acl_add_publish(struct ubusd_acl_file *file, const char *obj)
+{
+	struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
+
+	o->publish = true;
+}
+
+enum {
+	ACL_USER,
+	ACL_GROUP,
+	ACL_ACCESS,
+	ACL_PUBLISH,
+	ACL_SUBSCRIBE,
+	ACL_INHERIT,
+	__ACL_MAX
+};
+
+static const struct blobmsg_policy acl_policy[__ACL_MAX] = {
+	[ACL_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING },
+	[ACL_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING },
+	[ACL_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_TABLE },
+	[ACL_PUBLISH] = { .name = "publish", .type = BLOBMSG_TYPE_ARRAY },
+	[ACL_SUBSCRIBE] = { .name = "subscribe", .type = BLOBMSG_TYPE_ARRAY },
+	[ACL_INHERIT] = { .name = "inherit", .type = BLOBMSG_TYPE_ARRAY },
+};
+
+static void
+ubusd_acl_file_add(struct ubusd_acl_file *file)
+{
+	struct blob_attr *tb[__ACL_MAX], *cur;
+	int rem;
+
+	blobmsg_parse(acl_policy, __ACL_MAX, tb, blob_data(file->blob),
+		      blob_len(file->blob));
+
+	if (tb[ACL_USER])
+		file->user = blobmsg_get_string(tb[ACL_USER]);
+	else if (tb[ACL_GROUP])
+		file->group = blobmsg_get_string(tb[ACL_GROUP]);
+	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);
+
+	if (tb[ACL_SUBSCRIBE])
+		blobmsg_for_each_attr(cur, tb[ACL_SUBSCRIBE], rem)
+			if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
+				ubusd_acl_add_subscribe(file, blobmsg_get_string(cur));
+
+	if (tb[ACL_PUBLISH])
+		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));
+}
+
+static void
+ubusd_acl_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
+	struct vlist_node *node_old)
+{
+	struct ubusd_acl_file *file;
+
+	if (node_old) {
+		file = container_of(node_old, struct ubusd_acl_file, avl);
+		ubusd_acl_file_free(file);
+	}
+
+	if (node_new) {
+		file = container_of(node_new, struct ubusd_acl_file, avl);
+		ubusd_acl_file_add(file);
+	}
+}
+
+static struct ubus_msg_buf *
+ubusd_create_sequence_event_msg(void *priv, const char *id)
+{
+	void *s;
+
+	blob_buf_init(&b, 0);
+	blob_put_int32(&b, UBUS_ATTR_OBJID, 0);
+	blob_put_string(&b, UBUS_ATTR_METHOD, id);
+	s = blob_nest_start(&b, UBUS_ATTR_DATA);
+	blobmsg_add_u32(&b, "sequence", ubusd_acl_seq);
+	blob_nest_end(&b, s);
+
+	return ubus_msg_new(b.head, blob_raw_len(b.head), true);
+}
+
+static VLIST_TREE(ubusd_acl_files, avl_strcmp, ubusd_acl_update_cb, false, false);
+
+static int
+ubusd_acl_load_file(const char *filename)
+{
+	struct ubusd_acl_file *file;
+	void *blob;
+
+	blob_buf_init(&bbuf, 0);
+	if (!blobmsg_add_json_from_file(&bbuf, filename)) {
+		syslog(LOG_ERR, "failed to parse %s\n", filename);
+		return -1;
+	}
+
+	file = calloc_a(sizeof(*file), &blob, blob_raw_len(bbuf.head));
+	if (!file)
+		return -1;
+
+	file->blob = blob;
+
+	memcpy(blob, bbuf.head, blob_raw_len(bbuf.head));
+	INIT_LIST_HEAD(&file->acl);
+
+	vlist_add(&ubusd_acl_files, &file->avl, filename);
+	syslog(LOG_INFO, "loading %s\n", filename);
+
+	return 0;
+}
+
+void
+ubusd_acl_load(void)
+{
+	struct stat st;
+	glob_t gl;
+	int j;
+
+	if (glob("/usr/share/acl.d/*.json", GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
+		return;
+
+	vlist_update(&ubusd_acl_files);
+	for (j = 0; j < gl.gl_pathc; j++) {
+		if (stat(gl.gl_pathv[j], &st) || !S_ISREG(st.st_mode))
+			continue;
+
+		if (st.st_uid || st.st_gid) {
+			syslog(LOG_ERR, "%s has wrong owner\n", gl.gl_pathv[j]);
+			continue;
+		}
+		if (st.st_mode & (S_IWOTH | S_IWGRP | S_IXOTH)) {
+			syslog(LOG_ERR, "%s has wrong permissions\n", gl.gl_pathv[j]);
+			continue;
+		}
+		ubusd_acl_load_file(gl.gl_pathv[j]);
+	}
+
+	globfree(&gl);
+	vlist_flush(&ubusd_acl_files);
+	ubusd_acl_seq++;
+	ubusd_send_event(NULL, "ubus.acl.sequence", ubusd_create_sequence_event_msg, NULL);
+}
+
+static void
+ubusd_reply_add(struct ubus_object *obj)
+{
+	struct ubusd_acl_obj *acl;
+
+	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);
+
+			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);
+
+			if (acl->priv)
+				blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl",
+					blobmsg_data(acl->priv), blobmsg_data_len(acl->priv));
+
+			blobmsg_close_table(&b, c);
+		}
+		acl = avl_next_element(acl, avl);
+	}
+}
+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;
+	void *d, *a;
+
+	if (!attr[UBUS_ATTR_OBJID])
+		return UBUS_STATUS_INVALID_ARGUMENT;
+
+	obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID]));
+	if (!obj)
+		return UBUS_STATUS_NOT_FOUND;
+
+	blob_buf_init(&b, 0);
+	blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
+	d = blob_nest_start(&b, UBUS_ATTR_DATA);
+
+	blobmsg_add_u32(&b, "seq", ubusd_acl_seq);
+	a = blobmsg_open_array(&b, "acl");
+	list_for_each_entry(obj, &cl->objects, list)
+		ubusd_reply_add(obj);
+	blobmsg_close_table(&b, a);
+
+	blob_nest_end(&b, d);
+
+	ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA);
+
+	return 0;
+}
+
+static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg)
+{
+	if (!strcmp(method, "query"))
+		return ubusd_reply_query(cl, ub, ubus_parse_msg(ub->data), msg);
+
+	return UBUS_STATUS_INVALID_COMMAND;
+}
+
+void ubusd_acl_init(void)
+{
+	avl_init(&ubusd_acls, ubusd_acl_match_path, true, NULL);
+	acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL);
+	acl_obj->recv_msg = ubusd_acl_recv;
+}

+ 27 - 0
ubusd_acl.h

@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
+ *
+ * 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
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef __UBUSD_ACL_H
+#define __UBUSD_ACL_H
+
+enum ubusd_acl_type {
+	UBUS_ACL_PUBLISH,
+	UBUS_ACL_SUBSCRIBE,
+	UBUS_ACL_ACCESS,
+};
+
+int ubusd_acl_check(struct ubus_client *cl, const char *obj, const char *method, enum ubusd_acl_type type);
+int ubusd_acl_init_client(struct ubus_client *cl, int fd);
+void ubusd_acl_load(void);
+
+#endif

+ 4 - 0
ubusmsg.h

@@ -22,6 +22,7 @@
 #define UBUS_MSG_CHUNK_SIZE	65536
 
 #define UBUS_SYSTEM_OBJECT_EVENT	1
+#define UBUS_SYSTEM_OBJECT_ACL		2
 #define UBUS_SYSTEM_OBJECT_MAX		1024
 
 struct ubus_msghdr {
@@ -92,6 +93,9 @@ enum ubus_msg_attr {
 
 	UBUS_ATTR_SUBSCRIBERS,
 
+	UBUS_ATTR_USER,
+	UBUS_ATTR_GROUP,
+
 	/* must be last */
 	UBUS_ATTR_MAX,
 };