From 5ed9eb971504dbfb89f72ff96c3cdcae1b11c216 Mon Sep 17 00:00:00 2001 From: NADAL Jean-Baptiste Date: Fri, 26 Feb 2016 23:57:13 +0100 Subject: [PATCH] Merge Ubus with the 2016.02.26 version. --- 3P/ubus/CMakeLists.txt | 15 +- 3P/ubus/cli.c | 197 ++++++- 3P/ubus/examples/client.c | 31 +- 3P/ubus/examples/server.c | 25 +- 3P/ubus/libubus-acl.c | 153 ++++++ 3P/ubus/libubus-io.c | 15 +- 3P/ubus/libubus-obj.c | 6 +- 3P/ubus/libubus-req.c | 12 +- 3P/ubus/libubus.c | 26 +- 3P/ubus/libubus.h | 78 ++- 3P/ubus/libubus/builders/cmake/CMakeLists.txt | 2 +- 3P/ubus/lua/ubus.c | 28 +- 3P/ubus/ubusd.c | 24 +- 3P/ubus/ubusd.h | 19 + 3P/ubus/ubusd_acl.c | 495 ++++++++++++++++++ 3P/ubus/ubusd_acl.h | 28 + 3P/ubus/ubusd_event.c | 11 +- 3P/ubus/ubusd_monitor.c | 110 ++++ 3P/ubus/ubusd_obj.c | 8 + 3P/ubus/ubusd_obj.h | 3 +- 3P/ubus/ubusd_proto.c | 70 ++- 3P/ubus/ubusmsg.h | 19 + CMakeLists.txt | 4 +- 23 files changed, 1282 insertions(+), 97 deletions(-) create mode 100644 3P/ubus/libubus-acl.c create mode 100644 3P/ubus/ubusd_acl.c create mode 100644 3P/ubus/ubusd_acl.h create mode 100644 3P/ubus/ubusd_monitor.c diff --git a/3P/ubus/CMakeLists.txt b/3P/ubus/CMakeLists.txt index 6ebde7d4..b1fdd5b7 100644 --- a/3P/ubus/CMakeLists.txt +++ b/3P/ubus/CMakeLists.txt @@ -1,12 +1,11 @@ cmake_minimum_required(VERSION 2.6) PROJECT(ubus C) -# -Os -ADD_DEFINITIONS(-Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) +ADD_DEFINITIONS(-Os -Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) -OPTION(BUILD_LUA "build Lua plugin" OFF) -OPTION(BUILD_EXAMPLES "build examples" OFF) -OPTION(ENABLE_SYSTEMD "systemd support" OFF) +OPTION(BUILD_LUA "build Lua plugin" ON) +OPTION(BUILD_EXAMPLES "build examples" ON) +OPTION(ENABLE_SYSTEMD "systemd support" ON) SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "") SET(UBUS_UNIX_SOCKET "/var/run/ubus.sock") @@ -20,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 ubusd_monitor.c) +TARGET_LINK_LIBRARIES(ubusd ubox blobmsg_json ${json}) find_library(json NAMES json-c json) ADD_EXECUTABLE(cli cli.c) diff --git a/3P/ubus/cli.c b/3P/ubus/cli.c index c99a2d8e..98fc4fed 100644 --- a/3P/ubus/cli.c +++ b/3P/ubus/cli.c @@ -20,6 +20,21 @@ static struct blob_buf b; static int timeout = 30; static bool simple_output = false; static int verbose = 0; +static int monitor_dir = -1; +static uint32_t monitor_mask; +static const char * const monitor_types[] = { + [UBUS_MSG_HELLO] = "hello", + [UBUS_MSG_STATUS] = "status", + [UBUS_MSG_DATA] = "data", + [UBUS_MSG_PING] = "ping", + [UBUS_MSG_LOOKUP] = "lookup", + [UBUS_MSG_INVOKE] = "invoke", + [UBUS_MSG_ADD_OBJECT] = "add_object", + [UBUS_MSG_REMOVE_OBJECT] = "remove_object", + [UBUS_MSG_SUBSCRIBE] = "subscribe", + [UBUS_MSG_UNSUBSCRIBE] = "unsubscribe", + [UBUS_MSG_NOTIFY] = "notify", +}; static const char *format_type(void *priv, struct blob_attr *attr) { @@ -86,6 +101,7 @@ static void receive_event(struct ubus_context *ctx, struct ubus_event_handler *e str = blobmsg_format_json(msg, true); printf("{ \"%s\": %s }\n", type, str); + fflush(stdout); free(str); } @@ -129,10 +145,10 @@ static int ubus_cli_listen(struct ubus_context *ctx, int argc, char **argv) static struct ubus_event_handler listener; const char *event; int ret = 0; - + memset(&listener, 0, sizeof(listener)); listener.cb = receive_event; - + if (argc > 0) { event = argv[0]; } else { @@ -283,6 +299,160 @@ static int ubus_cli_wait_for(struct ubus_context *ctx, int argc, char **argv) return ret; } +static const char * +ubus_cli_msg_type(uint32_t type) +{ + const char *ret = NULL; + static char unk_type[16]; + + + if (type < ARRAY_SIZE(monitor_types)) + ret = monitor_types[type]; + + if (!ret) { + snprintf(unk_type, sizeof(unk_type), "%d", type); + ret = unk_type; + } + + return ret; +} + +static char * +ubus_cli_get_monitor_data(struct blob_attr *data) +{ + static const struct blob_attr_info policy[UBUS_ATTR_MAX] = { + [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_OBJPATH] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_OBJTYPE] = { .type = BLOB_ATTR_INT32 }, + [UBUS_ATTR_SIGNATURE] = { .type = BLOB_ATTR_NESTED }, + [UBUS_ATTR_DATA] = { .type = BLOB_ATTR_NESTED }, + [UBUS_ATTR_ACTIVE] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_NO_REPLY] = { .type = BLOB_ATTR_INT8 }, + [UBUS_ATTR_USER] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_GROUP] = { .type = BLOB_ATTR_STRING }, + }; + static const char * const names[UBUS_ATTR_MAX] = { + [UBUS_ATTR_STATUS] = "status", + [UBUS_ATTR_OBJPATH] = "objpath", + [UBUS_ATTR_OBJID] = "objid", + [UBUS_ATTR_METHOD] = "method", + [UBUS_ATTR_OBJTYPE] = "objtype", + [UBUS_ATTR_SIGNATURE] = "signature", + [UBUS_ATTR_DATA] = "data", + [UBUS_ATTR_ACTIVE] = "active", + [UBUS_ATTR_NO_REPLY] = "no_reply", + [UBUS_ATTR_USER] = "user", + [UBUS_ATTR_GROUP] = "group", + }; + struct blob_attr *tb[UBUS_ATTR_MAX]; + int i; + + blob_buf_init(&b, 0); + blob_parse(data, tb, policy, UBUS_ATTR_MAX); + + for (i = 0; i < UBUS_ATTR_MAX; i++) { + const char *n = names[i]; + struct blob_attr *v = tb[i]; + + if (!tb[i] || !n) + continue; + + switch(policy[i].type) { + case BLOB_ATTR_INT32: + blobmsg_add_u32(&b, n, blob_get_int32(v)); + break; + case BLOB_ATTR_STRING: + blobmsg_add_string(&b, n, blob_data(v)); + break; + case BLOB_ATTR_INT8: + blobmsg_add_u8(&b, n, !!blob_get_int8(v)); + break; + case BLOB_ATTR_NESTED: + blobmsg_add_field(&b, BLOBMSG_TYPE_TABLE, n, blobmsg_data(v), blobmsg_data_len(v)); + break; + } + } + + return blobmsg_format_json(b.head, true); +} + +static void +ubus_cli_monitor_cb(struct ubus_context *ctx, uint32_t seq, struct blob_attr *msg) +{ + static const struct blob_attr_info policy[UBUS_MONITOR_MAX] = { + [UBUS_MONITOR_CLIENT] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_PEER] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_SEND] = { .type = BLOB_ATTR_INT8 }, + [UBUS_MONITOR_TYPE] = { .type = BLOB_ATTR_INT32 }, + [UBUS_MONITOR_DATA] = { .type = BLOB_ATTR_NESTED }, + }; + struct blob_attr *tb[UBUS_MONITOR_MAX]; + uint32_t client, peer, type; + bool send; + char *data; + + blob_parse(msg, tb, policy, UBUS_MONITOR_MAX); + + if (!tb[UBUS_MONITOR_CLIENT] || + !tb[UBUS_MONITOR_PEER] || + !tb[UBUS_MONITOR_SEND] || + !tb[UBUS_MONITOR_TYPE] || + !tb[UBUS_MONITOR_DATA]) { + printf("Invalid monitor msg\n"); + return; + } + + send = blob_get_int32(tb[UBUS_MONITOR_SEND]); + client = blob_get_int32(tb[UBUS_MONITOR_CLIENT]); + peer = blob_get_int32(tb[UBUS_MONITOR_PEER]); + type = blob_get_int32(tb[UBUS_MONITOR_TYPE]); + + if (monitor_mask && type < 32 && !(monitor_mask & (1 << type))) + return; + + if (monitor_dir >= 0 && send != monitor_dir) + return; + + data = ubus_cli_get_monitor_data(tb[UBUS_MONITOR_DATA]); + printf("%s %08x #%08x %14s: %s\n", send ? "->" : "<-", client, peer, ubus_cli_msg_type(type), data); + free(data); + fflush(stdout); +} + +static int ubus_cli_monitor(struct ubus_context *ctx, int argc, char **argv) +{ + int ret; + + uloop_init(); + ubus_add_uloop(ctx); + ctx->monitor_cb = ubus_cli_monitor_cb; + ret = ubus_monitor_start(ctx); + if (ret) + return ret; + + uloop_run(); + uloop_done(); + + ubus_monitor_stop(ctx); + return 0; +} + +static int add_monitor_type(const char *type) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(monitor_types); i++) { + if (!monitor_types[i] || strcmp(monitor_types[i], type) != 0) + continue; + + monitor_mask |= 1 << i; + return 0; + } + + return -1; +} static int usage(const char *prog) { @@ -293,6 +463,9 @@ static int usage(const char *prog) " -t : Set the timeout (in seconds) for a command to complete\n" " -S: Use simplified output (for scripts)\n" " -v: More verbose output\n" + " -m : (for monitor): include a specific message type\n" + " (can be used more than once)\n" + " -M (for monitor): only capture received or transmitted traffic\n" "\n" "Commands:\n" " - list [] List objects\n" @@ -300,6 +473,7 @@ static int usage(const char *prog) " - listen [...] Listen for events\n" " - send [] Send an event\n" " - wait_for [...] Wait for multiple objects to appear on ubus\n" + " - monitor Monitor ubus traffic\n" "\n", prog); return 1; } @@ -314,6 +488,7 @@ struct { { "listen", ubus_cli_listen }, { "send", ubus_cli_send }, { "wait_for", ubus_cli_wait_for }, + { "monitor", ubus_cli_monitor }, }; int main(int argc, char **argv) @@ -326,7 +501,7 @@ int main(int argc, char **argv) progname = argv[0]; - while ((ch = getopt(argc, argv, "vs:t:S")) != -1) { + while ((ch = getopt(argc, argv, "m:M:vs:t:S")) != -1) { switch (ch) { case 's': ubus_socket = optarg; @@ -340,6 +515,22 @@ int main(int argc, char **argv) case 'v': verbose++; break; + case 'm': + if (add_monitor_type(optarg)) + return usage(progname); + break; + case 'M': + switch (optarg[0]) { + case 'r': + monitor_dir = 0; + break; + case 't': + monitor_dir = 1; + break; + default: + return usage(progname); + } + break; default: return usage(progname); } diff --git a/3P/ubus/examples/client.c b/3P/ubus/examples/client.c index 516d9d0b..7ef56634 100644 --- a/3P/ubus/examples/client.c +++ b/3P/ubus/examples/client.c @@ -92,34 +92,37 @@ static void test_count(struct uloop_timeout *timeout) COUNT_TO_MAX = 1000000, PROGRESSION = 100, }; - + uint32_t id; static uint32_t count_to = 100000; static int count_progression = PROGRESSION; char *s; - + if (count_to <= COUNT_TO_MIN) count_progression = PROGRESSION; else if (count_to >= COUNT_TO_MAX) count_progression = -PROGRESSION; - + count_to += count_progression; - + s = count_to_number(count_to); - if (!s) + if (!s) { fprintf(stderr, "Could not allocate memory to count up to '%u'\n", count_to); - + return; + } + fprintf(stderr, "Sending count up to '%u'; string has length '%u'\n", count_to, (uint32_t)strlen(s)); blob_buf_init(&b, 0); blobmsg_add_u32(&b, "to", count_to); blobmsg_add_string(&b, "string", s); - + if (ubus_lookup_id(ctx, "test", &id)) { + free(s); fprintf(stderr, "Failed to look up test object\n"); return; } - + ubus_invoke(ctx, id, "count", b.head, test_count_data_cb, &count_to, 5000); free(s); @@ -173,30 +176,30 @@ static void client_main(void) static struct ubus_request req; uint32_t id; int ret; - + ret = ubus_add_object(ctx, &test_client_object); if (ret) { fprintf(stderr, "Failed to add_object object: %s\n", ubus_strerror(ret)); return; } - + if (ubus_lookup_id(ctx, "test", &id)) { fprintf(stderr, "Failed to look up test object\n"); return; } - + blob_buf_init(&b, 0); blobmsg_add_u32(&b, "id", test_client_object.id); ubus_invoke(ctx, id, "watch", b.head, NULL, 0, 3000); test_client_notify_cb(¬ify_timer); - + blob_buf_init(&b, 0); blobmsg_add_string(&b, "msg", "blah"); ubus_invoke_async(ctx, id, "hello", b.head, &req); req.fd_cb = test_client_fd_cb; req.complete_cb = test_client_complete_cb; ubus_complete_request_async(ctx, &req); - + uloop_timeout_set(&count_timer, 2000); uloop_run(); @@ -206,7 +209,7 @@ int main(int argc, char **argv) { const char *ubus_socket = NULL; int ch; - + while ((ch = getopt(argc, argv, "cs:")) != -1) { switch (ch) { case 's': diff --git a/3P/ubus/examples/server.c b/3P/ubus/examples/server.c index 87da7705..e0cde0bf 100644 --- a/3P/ubus/examples/server.c +++ b/3P/ubus/examples/server.c @@ -61,11 +61,11 @@ static void test_hello_reply(struct uloop_timeout *t) { struct hello_request *req = container_of(t, struct hello_request, timeout); int fds[2]; - + blob_buf_init(&b, 0); blobmsg_add_string(&b, "message", req->data); ubus_send_reply(ctx, &req->req, b.head); - + if (pipe(fds) == -1) { fprintf(stderr, "Failed to create pipe\n"); return; @@ -73,7 +73,7 @@ static void test_hello_reply(struct uloop_timeout *t) ubus_request_set_fd(ctx, &req->req, fds[0]); ubus_complete_deferred_request(ctx, &req->req, 0); req->fd = fds[1]; - + req->timeout.cb = test_hello_fd_reply; test_hello_fd_reply(t); } @@ -86,18 +86,21 @@ static int test_hello(struct ubus_context *ctx, struct ubus_object *obj, struct blob_attr *tb[__HELLO_MAX]; const char *format = "%s received a message: %s"; const char *msgstr = "(unknown)"; - + blobmsg_parse(hello_policy, ARRAY_SIZE(hello_policy), tb, blob_data(msg), blob_len(msg)); - + if (tb[HELLO_MSG]) msgstr = blobmsg_data(tb[HELLO_MSG]); - + hreq = calloc(1, sizeof(*hreq) + strlen(format) + strlen(obj->name) + strlen(msgstr) + 1); + if (!hreq) + return UBUS_STATUS_UNKNOWN_ERROR; + sprintf(hreq->data, format, obj->name, msgstr); ubus_defer_request(ctx, req, &hreq->req); hreq->timeout.cb = test_hello_reply; uloop_timeout_set(&hreq->timeout, 1000); - + return 0; } @@ -248,13 +251,13 @@ int main(int argc, char **argv) fprintf(stderr, "Failed to connect to ubus\n"); return -1; } - + ubus_add_uloop(ctx); - + server_main(); - + ubus_free(ctx); uloop_done(); - + return 0; } diff --git a/3P/ubus/libubus-acl.c b/3P/ubus/libubus-acl.c new file mode 100644 index 00000000..0274520f --- /dev/null +++ b/3P/ubus/libubus-acl.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2015 John Cripin + * + * 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 + +#include +#include + +#include "libubus.h" +#include + +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; +} diff --git a/3P/ubus/libubus-io.c b/3P/ubus/libubus-io.c index 6500da2f..68b3d660 100644 --- a/3P/ubus/libubus-io.c +++ b/3P/ubus/libubus-io.c @@ -11,7 +11,7 @@ * GNU General Public License for more details. */ -// AWOX #define _GNU_SOURCE +#define _GNU_SOURCE #include #include #include @@ -54,7 +54,7 @@ static void wait_data(int fd, bool write) struct pollfd pfd = { .fd = fd }; pfd.events = write ? POLLOUT : POLLIN; - poll(&pfd, 1, 0); + poll(&pfd, 1, -1); } static int writev_retry(int fd, struct iovec *iov, int iov_len, int sock_fd) @@ -293,7 +293,8 @@ static bool get_next_msg(struct ubus_context *ctx, int *recv_fd) iov.iov_base = (char *)ctx->msgbuf.data + sizeof(hdrbuf.data); iov.iov_len = blob_len(ctx->msgbuf.data); - if (iov.iov_len > 0 && !recv_retry(ctx->sock.fd, &iov, true, NULL)) + if (iov.iov_len > 0 && + recv_retry(ctx->sock.fd, &iov, true, NULL) <= 0) return false; return true; @@ -321,7 +322,7 @@ void __hidden ubus_poll_data(struct ubus_context *ctx, int timeout) .events = POLLIN | POLLERR, }; - poll(&pfd, 1, timeout); + poll(&pfd, 1, timeout ? timeout : -1); ubus_handle_data(&ctx->sock, ULOOP_READ); } @@ -393,10 +394,8 @@ int ubus_reconnect(struct ubus_context *ctx, const char *path) goto out_free; ret = UBUS_STATUS_OK; - //fcntl(ctx->sock.fd, F_SETFL, fcntl(ctx->sock.fd, F_GETFL) | O_NONBLOCK | O_CLOEXEC); - // AWOX M2: Should add F_SETFD: - fcntl(ctx->sock.fd, F_SETFL, fcntl(ctx->sock.fd, F_GETFL) | O_NONBLOCK); - + fcntl(ctx->sock.fd, F_SETFL, fcntl(ctx->sock.fd, F_GETFL) | O_NONBLOCK | O_CLOEXEC); + ubus_refresh_state(ctx); out_free: diff --git a/3P/ubus/libubus-obj.c b/3P/ubus/libubus-obj.c index 8bc83dc6..990d04bd 100644 --- a/3P/ubus/libubus-obj.c +++ b/3P/ubus/libubus-obj.c @@ -69,7 +69,11 @@ ubus_process_invoke(struct ubus_context *ctx, struct ubus_msghdr *hdr, req.peer = hdr->peer; req.seq = hdr->seq; req.object = obj->id; - + if (attrbuf[UBUS_ATTR_USER] && attrbuf[UBUS_ATTR_GROUP]) { + req.acl.user = blobmsg_get_string(attrbuf[UBUS_ATTR_USER]); + req.acl.group = blobmsg_get_string(attrbuf[UBUS_ATTR_GROUP]); + req.acl.object = obj->name; + } for (method = 0; method < obj->n_methods; method++) if (!obj->methods[method].name || !strcmp(obj->methods[method].name, diff --git a/3P/ubus/libubus-req.c b/3P/ubus/libubus-req.c index d44db51f..416adaba 100644 --- a/3P/ubus/libubus-req.c +++ b/3P/ubus/libubus-req.c @@ -160,6 +160,10 @@ int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, ubus_poll_data(ctx, (unsigned int) timeout); uloop_cancelled = cancelled; + if (ctx->sock.eof) { + ubus_set_req_status(req, UBUS_STATUS_CONNECTION_FAILED); + break; + } } ctx->stack_depth--; if (ctx->stack_depth) @@ -175,7 +179,7 @@ int ubus_complete_request(struct ubus_context *ctx, struct ubus_request *req, if (!registered) { uloop_fd_delete(&ctx->sock); - if (ctx->stack_depth) + if (!ctx->stack_depth) ctx->pending_timer.cb(&ctx->pending_timer); } @@ -466,3 +470,9 @@ void __hidden ubus_process_req_msg(struct ubus_context *ctx, struct ubus_msghdr_ break; } } + +int __ubus_monitor(struct ubus_context *ctx, const char *type) +{ + blob_buf_init(&b, 0); + return ubus_invoke(ctx, UBUS_SYSTEM_OBJECT_MONITOR, type, b.head, NULL, NULL, 1000); +} diff --git a/3P/ubus/libubus.c b/3P/ubus/libubus.c index de1a6dd9..c8ef7f7c 100644 --- a/3P/ubus/libubus.c +++ b/3P/ubus/libubus.c @@ -81,7 +81,7 @@ ubus_queue_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf) pending->hdr.data = data; memcpy(&pending->hdr.hdr, &buf->hdr, sizeof(buf->hdr)); memcpy(data, buf->data, blob_raw_len(buf->data)); - list_add(&pending->list, &ctx->pending); + list_add_tail(&pending->list, &ctx->pending); if (ctx->sock.registered) uloop_timeout_set(&ctx->pending_timer, 1); } @@ -105,6 +105,10 @@ ubus_process_msg(struct ubus_context *ctx, struct ubus_msghdr_buf *buf, int fd) ubus_process_obj_msg(ctx, buf); break; + case UBUS_MSG_MONITOR: + if (ctx->monitor_cb) + ctx->monitor_cb(ctx, buf->hdr.seq, buf->data); + break; } } @@ -192,8 +196,7 @@ int ubus_lookup_id(struct ubus_context *ctx, const char *path, uint32_t *id) req.raw_data_cb = ubus_lookup_id_cb; req.priv = id; - // Awox Remomve infinite timeout: return ubus_complete_request(ctx, &req, 0); - return ubus_complete_request(ctx, &req, 5000); + return ubus_complete_request(ctx, &req, 0); } static int ubus_event_cb(struct ubus_context *ctx, struct ubus_object *obj, @@ -272,8 +275,10 @@ static void ubus_default_connection_lost(struct ubus_context *ctx) uloop_end(); } -static int _ubus_connect(struct ubus_context *ctx, const char *path) +int ubus_connect_ctx(struct ubus_context *ctx, const char *path) { + memset(ctx, 0, sizeof(*ctx)); + ctx->sock.fd = -1; ctx->sock.cb = ubus_handle_data; ctx->connection_lost = ubus_default_connection_lost; @@ -317,7 +322,7 @@ static void ubus_auto_connect_cb(struct uloop_timeout *timeout) { struct ubus_auto_conn *conn = container_of(timeout, struct ubus_auto_conn, timer); - if (_ubus_connect(&conn->ctx, conn->path)) { + if (ubus_connect_ctx(&conn->ctx, conn->path)) { uloop_timeout_set(timeout, 1000); fprintf(stderr, "failed to connect to ubus\n"); return; @@ -342,7 +347,7 @@ struct ubus_context *ubus_connect(const char *path) if (!ctx) return NULL; - if (_ubus_connect(ctx, path)) { + if (ubus_connect_ctx(ctx, path)) { free(ctx); ctx = NULL; } @@ -350,10 +355,17 @@ struct ubus_context *ubus_connect(const char *path) return ctx; } -void ubus_free(struct ubus_context *ctx) +void ubus_shutdown(struct ubus_context *ctx) { blob_buf_free(&b); + if (!ctx) + return; close(ctx->sock.fd); free(ctx->msgbuf.data); +} + +void ubus_free(struct ubus_context *ctx) +{ + ubus_shutdown(ctx); free(ctx); } diff --git a/3P/ubus/libubus.h b/3P/ubus/libubus.h index 54f4436b..07239d66 100644 --- a/3P/ubus/libubus.h +++ b/3P/ubus/libubus.h @@ -66,32 +66,44 @@ typedef void (*ubus_connect_handler_t)(struct ubus_context *ctx); .methods = _methods \ } -#define __UBUS_METHOD_NOARG(_name, _handler) \ +#define __UBUS_METHOD_NOARG(_name, _handler, _tags) \ .name = _name, \ - .handler = _handler + .handler = _handler, \ + .tags = _tags -#define __UBUS_METHOD(_name, _handler, _policy) \ - __UBUS_METHOD_NOARG(_name, _handler), \ +#define __UBUS_METHOD(_name, _handler, _policy, _tags) \ + __UBUS_METHOD_NOARG(_name, _handler, _tags), \ .policy = _policy, \ .n_policy = ARRAY_SIZE(_policy) #define UBUS_METHOD(_name, _handler, _policy) \ - { __UBUS_METHOD(_name, _handler, _policy) } + { __UBUS_METHOD(_name, _handler, _policy, 0) } + +#define UBUS_METHOD_TAG(_name, _handler, _policy, _tags)\ + { __UBUS_METHOD(_name, _handler, _policy, _tags) } #define UBUS_METHOD_MASK(_name, _handler, _policy, _mask) \ { \ - __UBUS_METHOD(_name, _handler, _policy),\ + __UBUS_METHOD(_name, _handler, _policy, 0),\ .mask = _mask \ } #define UBUS_METHOD_NOARG(_name, _handler) \ - { __UBUS_METHOD_NOARG(_name, _handler) } + { __UBUS_METHOD_NOARG(_name, _handler, 0) } + +#define UBUS_METHOD_TAG_NOARG(_name, _handler, _tags) \ + { __UBUS_METHOD_NOARG(_name, _handler, _tags) } + +#define UBUS_TAG_STATUS BIT(0) +#define UBUS_TAG_ADMIN BIT(1) +#define UBUS_TAG_PRIVATE BIT(2) struct ubus_method { const char *name; ubus_handler_t handler; unsigned long mask; + unsigned long tags; const struct blobmsg_policy *policy; int n_policy; }; @@ -131,7 +143,6 @@ struct ubus_event_handler { struct ubus_object obj; ubus_event_handler_t cb; - void *priv; }; struct ubus_context { @@ -147,6 +158,7 @@ struct ubus_context { int stack_depth; void (*connection_lost)(struct ubus_context *ctx); + void (*monitor_cb)(struct ubus_context *ctx, uint32_t seq, struct blob_attr *data); struct ubus_msghdr_buf msgbuf; uint32_t msgbuf_data_len; @@ -160,11 +172,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; @@ -210,10 +230,22 @@ struct ubus_auto_conn { }; struct ubus_context *ubus_connect(const char *path); +int ubus_connect_ctx(struct ubus_context *ctx, const char *path); void ubus_auto_connect(struct ubus_auto_conn *conn); int ubus_reconnect(struct ubus_context *ctx, const char *path); + +/* call this only for struct ubus_context pointers returned by ubus_connect() */ void ubus_free(struct ubus_context *ctx); +/* call this only for struct ubus_context pointers initialised by ubus_connect_ctx() */ +void ubus_shutdown(struct ubus_context *ctx); + +static inline void ubus_auto_shutdown(struct ubus_auto_conn *conn) +{ + uloop_timeout_cancel(&conn->timer); + ubus_shutdown(&conn->ctx); +} + const char *ubus_strerror(int error); static inline void ubus_add_uloop(struct ubus_context *ctx) @@ -265,6 +297,34 @@ 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); +int __ubus_monitor(struct ubus_context *ctx, const char *type); + +static inline int ubus_monitor_start(struct ubus_context *ctx) +{ + return __ubus_monitor(ctx, "add"); +} + +static inline int ubus_monitor_stop(struct ubus_context *ctx) +{ + return __ubus_monitor(ctx, "remove"); +} + + +/* ----------- 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 */ @@ -284,6 +344,7 @@ static inline void ubus_defer_request(struct ubus_context *ctx, struct ubus_request_data *req, struct ubus_request_data *new_req) { + (void) ctx; memcpy(new_req, req, sizeof(*req)); req->deferred = true; } @@ -291,6 +352,7 @@ static inline void ubus_defer_request(struct ubus_context *ctx, static inline void ubus_request_set_fd(struct ubus_context *ctx, struct ubus_request_data *req, int fd) { + (void) ctx; req->fd = fd; } diff --git a/3P/ubus/libubus/builders/cmake/CMakeLists.txt b/3P/ubus/libubus/builders/cmake/CMakeLists.txt index 5b22540b..a85adba5 100644 --- a/3P/ubus/libubus/builders/cmake/CMakeLists.txt +++ b/3P/ubus/libubus/builders/cmake/CMakeLists.txt @@ -16,9 +16,9 @@ file( ../../../libubus-obj.c ../../../libubus-sub.c ../../../libubus-req.c + ../../../libubus-acl.c ) - ADD_DEFINITIONS(-Wall -Werror --std=gnu99 -g3 -Wmissing-declarations) diff --git a/3P/ubus/lua/ubus.c b/3P/ubus/lua/ubus.c index 92fb0a15..86e34b7c 100644 --- a/3P/ubus/lua/ubus.c +++ b/3P/ubus/lua/ubus.c @@ -302,8 +302,9 @@ ubus_method_handler(struct ubus_context *ctx, struct ubus_object *obj, lua_call(state, 2, 1); if (lua_isnumber(state, -1)) rv = lua_tonumber(state, -1); - } else - lua_pop(state, 1); + } + + lua_pop(state, 1); return rv; } @@ -382,6 +383,9 @@ static int ubus_lua_load_methods(lua_State *L, struct ubus_method *m) /* setup the policy pointers */ p = malloc(sizeof(struct blobmsg_policy) * plen); + if (!p) + return 1; + memset(p, 0, sizeof(struct blobmsg_policy) * plen); m->policy = p; lua_pushnil(L); @@ -417,6 +421,9 @@ static struct ubus_object* ubus_lua_load_object(lua_State *L) /* setup object pointers */ obj = malloc(sizeof(struct ubus_lua_object)); + if (!obj) + return NULL; + memset(obj, 0, sizeof(struct ubus_lua_object)); obj->o.name = lua_tostring(L, -2); @@ -427,6 +434,11 @@ static struct ubus_object* ubus_lua_load_object(lua_State *L) /* setup type pointers */ obj->o.type = malloc(sizeof(struct ubus_object_type)); + if (!obj->o.type) { + free(obj); + return NULL; + } + memset(obj->o.type, 0, sizeof(struct ubus_object_type)); obj->o.type->name = lua_tostring(L, -2); obj->o.type->id = 0; @@ -529,10 +541,11 @@ ubus_lua_call_cb(struct ubus_request *req, int type, struct blob_attr *msg) { lua_State *L = (lua_State *)req->priv; - if (!msg) + if (!msg && L) lua_pushnil(L); - ubus_lua_parse_blob_array(L, blob_data(msg), blob_len(msg), true); + if (msg && L) + ubus_lua_parse_blob_array(L, blob_data(msg), blob_len(msg), true); } static int @@ -585,10 +598,13 @@ ubus_event_handler(struct ubus_context *ctx, struct ubus_event_handler *ev, lua_getglobal(state, "__ubus_cb_event"); lua_rawgeti(state, -1, listener->r); + lua_remove(state, -2); if (lua_isfunction(state, -1)) { ubus_lua_parse_blob_array(state, blob_data(msg), blob_len(msg), true); lua_call(state, 1, 0); + } else { + lua_pop(state, 1); } } @@ -598,6 +614,9 @@ ubus_lua_load_event(lua_State *L) struct ubus_lua_event* event = NULL; event = malloc(sizeof(struct ubus_lua_event)); + if (!event) + return NULL; + memset(event, 0, sizeof(struct ubus_lua_event)); event->e.cb = ubus_event_handler; @@ -666,6 +685,7 @@ ubus_lua__gc(lua_State *L) { struct ubus_lua_connection *c = luaL_checkudata(L, 1, METANAME); + blob_buf_free(&c->buf); if (c->ctx != NULL) { ubus_free(c->ctx); diff --git a/3P/ubus/ubusd.c b/3P/ubus/ubusd.c index 89031053..f1f8ac7f 100644 --- a/3P/ubus/ubusd.c +++ b/3P/ubus/ubusd.c @@ -17,6 +17,7 @@ #ifdef FreeBSD #include #endif +#include #include #include #include @@ -135,6 +136,9 @@ void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free) { int written; + if (ub->hdr.type != UBUS_MSG_MONITOR) + ubusd_monitor_message(cl, ub, true); + if (!cl->tx_queue[cl->txq_cur]) { written = ubus_msg_writev(cl->sock.fd, ub, 0); if (written >= ub->len + sizeof(ub->hdr)) @@ -178,6 +182,7 @@ static void handle_client_disconnect(struct ubus_client *cl) while (ubus_msg_head(cl)) ubus_msg_dequeue(cl); + ubusd_monitor_disconnect(cl); ubusd_proto_free_client(cl); if (cl->pending_msg_fd >= 0) close(cl->pending_msg_fd); @@ -241,7 +246,7 @@ retry: fd_buf.fd = -1; - iov.iov_base = &cl->hdrbuf + offset; + iov.iov_base = ((char *) &cl->hdrbuf) + offset; iov.iov_len = sizeof(cl->hdrbuf) - offset; if (cl->pending_msg_fd < 0) { @@ -296,6 +301,7 @@ retry: cl->pending_msg_fd = -1; cl->pending_msg_offset = 0; cl->pending_msg = NULL; + ubusd_monitor_message(cl, ub, false); ubusd_proto_receive_message(cl, ub); goto retry; } @@ -350,11 +356,17 @@ static int usage(const char *progname) { fprintf(stderr, "Usage: %s []\n" "Options: \n" + " -A : Set the path to ACL files\n" " -s : Set the unix domain socket to listen on\n" "\n", progname); return 1; } +static void sighup_handler(int sig) +{ + ubusd_acl_load(); +} + int main(int argc, char **argv) { const char *ubus_socket = UBUS_UNIX_SOCKET; @@ -362,21 +374,26 @@ int main(int argc, char **argv) int ch; signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, sighup_handler); + openlog("ubusd", LOG_PID, LOG_DAEMON); uloop_init(); - while ((ch = getopt(argc, argv, "s:")) != -1) { + while ((ch = getopt(argc, argv, "A:s:")) != -1) { switch (ch) { case 's': ubus_socket = optarg; break; + case 'A': + ubusd_acl_dir = optarg; + break; default: return usage(argv[0]); } } unlink(ubus_socket); - umask(0177); + umask(0111); server_fd.fd = usock(USOCK_UNIX | USOCK_SERVER | USOCK_NONBLOCK, ubus_socket, NULL); if (server_fd.fd < 0) { perror("usock"); @@ -384,6 +401,7 @@ int main(int argc, char **argv) goto out; } uloop_fd_add(&server_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); + ubusd_acl_load(); uloop_run(); unlink(ubus_socket); diff --git a/3P/ubus/ubusd.h b/3P/ubus/ubusd.h index 9cdb9d5c..b5226900 100644 --- a/3P/ubus/ubusd.h +++ b/3P/ubus/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]; @@ -58,17 +64,30 @@ struct ubus_path { const char name[]; }; +extern const char *ubusd_acl_dir; + struct ubus_msg_buf *ubus_msg_new(void *data, int len, bool shared); void ubus_msg_send(struct ubus_client *cl, struct ubus_msg_buf *ub, bool free); void ubus_msg_free(struct ubus_msg_buf *ub); +struct blob_attr **ubus_parse_msg(struct blob_attr *msg); struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb); void ubusd_proto_receive_message(struct ubus_client *cl, struct ubus_msg_buf *ub); void ubusd_proto_free_client(struct ubus_client *cl); +void ubus_proto_send_msg_from_blob(struct ubus_client *cl, struct ubus_msg_buf *ub, + uint8_t type); +typedef struct ubus_msg_buf *(*event_fill_cb)(void *priv, const char *id); void ubusd_event_init(void); void ubusd_event_cleanup_object(struct ubus_object *obj); 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); + +void ubusd_monitor_init(void); +void ubusd_monitor_message(struct ubus_client *cl, struct ubus_msg_buf *ub, bool send); +void ubusd_monitor_disconnect(struct ubus_client *cl); #endif diff --git a/3P/ubus/ubusd_acl.c b/3P/ubus/ubusd_acl.c new file mode 100644 index 00000000..85ada5dc --- /dev/null +++ b/3P/ubus/ubusd_acl.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2015 John Crispin + * + * 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 +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ubusd.h" + +#ifndef SO_PEERCRED +struct ucred { + int pid; + int uid; + int gid; +}; +#endif + +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; +}; + +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) +{ + 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->uid) + return 0; + + acl = avl_find_ge_element(&ubusd_acls, obj, acl, avl); + if (!acl) + return -1; + + avl_for_element_to_last(&ubusd_acls, acl, acl, avl) { + int diff = ubusd_acl_match_path(obj, acl->avl.key, NULL); + + if (diff) + break; + + if (ubusd_acl_match_cred(cl, acl)) + 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; + } + } + + return -1; +} + +int +ubusd_acl_init_client(struct ubus_client *cl, int fd) +{ + struct ucred cred; + struct passwd *pwd; + struct group *group; + +#ifdef SO_PEERCRED + unsigned int len = sizeof(struct ucred); + + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) + return -1; +#else + memset(&cred, 0, sizeof(cred)); +#endif + + 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; +} + +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) +{ + 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(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]) + 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; + const char *suffix = "/*.json"; + char *path = alloca(strlen(ubusd_acl_dir) + strlen(suffix) + 1); + + sprintf(path, "%s%s", ubusd_acl_dir, suffix); + if (glob(path, 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); + if (!acl) + return; + + avl_for_element_to_last(&ubusd_acls, acl, acl, avl) { + void *c; + + if (!acl->priv) + continue; + + if (!ubusd_acl_match_path(obj->path.key, acl->avl.key, NULL)) + continue; + + 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; + 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; +} diff --git a/3P/ubus/ubusd_acl.h b/3P/ubus/ubusd_acl.h new file mode 100644 index 00000000..c5dfd8a9 --- /dev/null +++ b/3P/ubus/ubusd_acl.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2015 John Crispin + * + * 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_free_client(struct ubus_client *cl); +void ubusd_acl_load(void); + +#endif diff --git a/3P/ubus/ubusd_event.c b/3P/ubus/ubusd_event.c index 8ec92018..55e4949e 100644 --- a/3P/ubus/ubusd_event.c +++ b/3P/ubus/ubusd_event.c @@ -103,8 +103,6 @@ static int ubusd_alloc_event_pattern(struct ubus_client *cl, struct blob_attr *m return 0; } -typedef struct ubus_msg_buf *(*event_fill_cb)(void *priv, const char *id); - static void ubusd_send_event_msg(struct ubus_msg_buf **ub, struct ubus_client *cl, struct ubus_object *obj, const char *id, event_fill_cb fill_cb, void *cb_priv) @@ -143,8 +141,8 @@ static bool strmatch_len(const char *s1, const char *s2, int *len) return false; } -static int ubusd_send_event(struct ubus_client *cl, const char *id, - event_fill_cb fill_cb, void *cb_priv) +int ubusd_send_event(struct ubus_client *cl, const char *id, + event_fill_cb fill_cb, void *cb_priv) { struct ubus_msg_buf *ub = NULL; struct event_source *ev; @@ -228,7 +226,7 @@ static int ubusd_forward_event(struct ubus_client *cl, struct blob_attr *msg) return ubusd_send_event(cl, id, ubusd_create_event_from_msg, data); } -static int ubusd_event_recv(struct ubus_client *cl, const char *method, struct blob_attr *msg) +static int ubusd_event_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg) { if (!strcmp(method, "register")) return ubusd_alloc_event_pattern(cl, msg); @@ -267,6 +265,7 @@ void ubusd_event_init(void) { ubus_init_string_tree(&patterns, true); event_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_EVENT); - event_obj->recv_msg = ubusd_event_recv; + if (event_obj != NULL) + event_obj->recv_msg = ubusd_event_recv; } diff --git a/3P/ubus/ubusd_monitor.c b/3P/ubus/ubusd_monitor.c new file mode 100644 index 00000000..82d03330 --- /dev/null +++ b/3P/ubus/ubusd_monitor.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2015 Felix Fietkau + * + * 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 "ubusd.h" + +static struct ubus_object *monitor_obj; +static LIST_HEAD(monitors); + +struct ubus_monitor { + struct list_head list; + struct ubus_client *cl; + uint32_t seq; +}; + +static void +ubusd_monitor_free(struct ubus_monitor *m) +{ + list_del(&m->list); + free(m); +} + +static void +ubusd_monitor_connect(struct ubus_client *cl, struct ubus_msg_buf *ub) +{ + struct ubus_monitor *m; + + ubusd_monitor_disconnect(cl); + + m = calloc(1, sizeof(*m)); + m->cl = cl; + list_add(&m->list, &monitors); +} + +void +ubusd_monitor_disconnect(struct ubus_client *cl) +{ + struct ubus_monitor *m; + + list_for_each_entry(m, &monitors, list) { + if (m->cl != cl) + continue; + + ubusd_monitor_free(m); + return; + } +} + +void +ubusd_monitor_message(struct ubus_client *cl, struct ubus_msg_buf *ub, bool send) +{ + static struct blob_buf mb; + struct ubus_monitor *m; + + if (list_empty(&monitors)) + return; + + blob_buf_init(&mb, 0); + blob_put_int32(&mb, UBUS_MONITOR_CLIENT, cl->id.id); + blob_put_int32(&mb, UBUS_MONITOR_PEER, ub->hdr.peer); + blob_put_int32(&mb, UBUS_MONITOR_SEQ, ub->hdr.seq); + blob_put_int32(&mb, UBUS_MONITOR_TYPE, ub->hdr.type); + blob_put_int8(&mb, UBUS_MONITOR_SEND, send); + blob_put(&mb, UBUS_MONITOR_DATA, blob_data(ub->data), blob_len(ub->data)); + + list_for_each_entry(m, &monitors, list) { + ub = ubus_msg_new(mb.head, blob_raw_len(mb.head), true); + ub->hdr.type = UBUS_MSG_MONITOR; + ub->hdr.seq = ++m->seq; + ubus_msg_send(m->cl, ub, true); + } +} + +static int +ubusd_monitor_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, + const char *method, struct blob_attr *msg) +{ + /* Only root is allowed for now */ + if (cl->uid != 0 || cl->gid != 0) + return UBUS_STATUS_PERMISSION_DENIED; + + if (!strcmp(method, "add")) { + ubusd_monitor_connect(cl, ub); + return 0; + } + + if (!strcmp(method, "remove")) { + ubusd_monitor_disconnect(cl); + return 0; + } + + return UBUS_STATUS_METHOD_NOT_FOUND; +} + +void +ubusd_monitor_init(void) +{ + monitor_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_MONITOR); + if (monitor_obj != NULL) + monitor_obj->recv_msg = ubusd_monitor_recv; +} diff --git a/3P/ubus/ubusd_obj.c b/3P/ubus/ubusd_obj.c index bef026a1..2b8f10f9 100644 --- a/3P/ubus/ubusd_obj.c +++ b/3P/ubus/ubusd_obj.c @@ -58,6 +58,9 @@ static struct ubus_object_type *ubus_create_obj_type(struct blob_attr *sig) int rem; type = calloc(1, sizeof(*type)); + if (!type) + return NULL; + type->refcount = 1; if (!ubus_alloc_id(&obj_types, &type->id, 0)) @@ -142,6 +145,9 @@ struct ubus_object *ubusd_create_object(struct ubus_client *cl, struct blob_attr return NULL; if (attr[UBUS_ATTR_OBJPATH]) { + if (ubusd_acl_check(cl, blob_data(attr[UBUS_ATTR_OBJPATH]), NULL, UBUS_ACL_PUBLISH)) + goto free; + obj->path.key = strdup(blob_data(attr[UBUS_ATTR_OBJPATH])); if (!obj->path.key) goto free; @@ -225,4 +231,6 @@ static void __constructor ubusd_obj_init(void) ubus_init_id_tree(&obj_types); ubus_init_string_tree(&path, false); ubusd_event_init(); + ubusd_acl_init(); + ubusd_monitor_init(); } diff --git a/3P/ubus/ubusd_obj.h b/3P/ubus/ubusd_obj.h index 9a119402..44740c8b 100644 --- a/3P/ubus/ubusd_obj.h +++ b/3P/ubus/ubusd_obj.h @@ -52,7 +52,8 @@ struct ubus_object { struct avl_node path; struct ubus_client *client; - int (*recv_msg)(struct ubus_client *client, const char *method, struct blob_attr *msg); + int (*recv_msg)(struct ubus_client *client, struct ubus_msg_buf *ub, + const char *method, struct blob_attr *msg); int event_seen; unsigned int invoke_seq; diff --git a/3P/ubus/ubusd_proto.c b/3P/ubus/ubusd_proto.c index aa08bca0..668f1ea8 100644 --- a/3P/ubus/ubusd_proto.c +++ b/3P/ubus/ubusd_proto.c @@ -32,9 +32,11 @@ static const struct blob_attr_info ubus_policy[UBUS_ATTR_MAX] = { [UBUS_ATTR_OBJID] = { .type = BLOB_ATTR_INT32 }, [UBUS_ATTR_STATUS] = { .type = BLOB_ATTR_INT32 }, [UBUS_ATTR_METHOD] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_USER] = { .type = BLOB_ATTR_STRING }, + [UBUS_ATTR_GROUP] = { .type = BLOB_ATTR_STRING }, }; -static struct blob_attr **ubus_parse_msg(struct blob_attr *msg) +struct blob_attr **ubus_parse_msg(struct blob_attr *msg) { blob_parse(msg, attrbuf, ubus_policy, UBUS_ATTR_MAX); return attrbuf; @@ -74,8 +76,8 @@ static struct ubus_msg_buf *ubus_reply_from_blob(struct ubus_msg_buf *ub, bool s return new; } -static void -ubus_send_msg_from_blob(struct ubus_client *cl, struct ubus_msg_buf *ub, +void +ubus_proto_send_msg_from_blob(struct ubus_client *cl, struct ubus_msg_buf *ub, uint8_t type) { ub = ubus_reply_from_blob(ub, true); @@ -129,7 +131,7 @@ static int ubusd_handle_remove_object(struct ubus_client *cl, struct ubus_msg_bu blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); ubusd_free_object(obj); - ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); return 0; } @@ -147,28 +149,33 @@ static int ubusd_handle_add_object(struct ubus_client *cl, struct ubus_msg_buf * if (attr[UBUS_ATTR_SIGNATURE]) blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); - ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); return 0; } static void ubusd_send_obj(struct ubus_client *cl, struct ubus_msg_buf *ub, struct ubus_object *obj) { struct ubus_method *m; + int cnt = 0; void *s; blob_buf_init(&b, 0); - if (obj->path.key) - blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->path.key); + blob_put_string(&b, UBUS_ATTR_OBJPATH, obj->path.key); blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); blob_put_int32(&b, UBUS_ATTR_OBJTYPE, obj->type->id.id); s = blob_nest_start(&b, UBUS_ATTR_SIGNATURE); - list_for_each_entry(m, &obj->type->methods, list) - blobmsg_add_blob(&b, m->data); + list_for_each_entry(m, &obj->type->methods, list) { + if (!ubusd_acl_check(cl, obj->path.key, blobmsg_name(m->data), UBUS_ACL_ACCESS)) { + blobmsg_add_blob(&b, m->data); + cnt++; + } + } blob_nest_end(&b, s); - ubus_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); + if (cnt) + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA); } static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) @@ -216,15 +223,20 @@ static int ubusd_handle_lookup(struct ubus_client *cl, struct ubus_msg_buf *ub, } static void -ubusd_forward_invoke(struct ubus_object *obj, const char *method, - struct ubus_msg_buf *ub, struct blob_attr *data) +ubusd_forward_invoke(struct ubus_client *cl, struct ubus_object *obj, + const char *method, struct ubus_msg_buf *ub, + struct blob_attr *data) { blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id); blob_put_string(&b, UBUS_ATTR_METHOD, method); + if (cl->user) + blob_put_string(&b, UBUS_ATTR_USER, cl->user); + if (cl->group) + blob_put_string(&b, UBUS_ATTR_GROUP, cl->group); if (data) blob_put(&b, UBUS_ATTR_DATA, blob_data(data), blob_len(data)); - ubus_send_msg_from_blob(obj->client, ub, UBUS_MSG_INVOKE); + ubus_proto_send_msg_from_blob(obj->client, ub, UBUS_MSG_INVOKE); } static int ubusd_handle_invoke(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr) @@ -244,12 +256,16 @@ static int ubusd_handle_invoke(struct ubus_client *cl, struct ubus_msg_buf *ub, method = blob_data(attr[UBUS_ATTR_METHOD]); + if (ubusd_acl_check(cl, obj->path.key, method, UBUS_ACL_ACCESS)) + return UBUS_STATUS_PERMISSION_DENIED; + if (!obj->client) - return obj->recv_msg(cl, method, attr[UBUS_ATTR_DATA]); + return obj->recv_msg(cl, ub, method, attr[UBUS_ATTR_DATA]); ub->hdr.peer = cl->id.id; blob_buf_init(&b, 0); - ubusd_forward_invoke(obj, method, ub, attr[UBUS_ATTR_DATA]); + + ubusd_forward_invoke(cl, obj, method, ub, attr[UBUS_ATTR_DATA]); ubus_msg_free(ub); return -1; @@ -287,7 +303,7 @@ static int ubusd_handle_notify(struct ubus_client *cl, struct ubus_msg_buf *ub, } blob_nest_end(&b, c); blob_put_int32(&b, UBUS_ATTR_STATUS, 0); - ubus_send_msg_from_blob(cl, ub, UBUS_MSG_STATUS); + ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_STATUS); } ub->hdr.peer = cl->id.id; @@ -296,7 +312,7 @@ static int ubusd_handle_notify(struct ubus_client *cl, struct ubus_msg_buf *ub, blob_buf_init(&b, 0); if (no_reply) blob_put_int8(&b, UBUS_ATTR_NO_REPLY, 1); - ubusd_forward_invoke(s->subscriber, method, ub, attr[UBUS_ATTR_DATA]); + ubusd_forward_invoke(cl, s->subscriber, method, ub, attr[UBUS_ATTR_DATA]); } ubus_msg_free(ub); @@ -364,6 +380,13 @@ static int ubusd_handle_add_watch(struct ubus_client *cl, struct ubus_msg_buf *u if (cl == target->client) return UBUS_STATUS_INVALID_ARGUMENT; + if (!target->path.key) { + if (strcmp(target->client->user, cl->user) && strcmp(target->client->group, cl->group)) + return UBUS_STATUS_NOT_FOUND; + } else if (ubusd_acl_check(cl, target->path.key, NULL, UBUS_ACL_SUBSCRIBE)) { + return UBUS_STATUS_NOT_FOUND; + } + ubus_subscribe(obj, target); return 0; } @@ -445,6 +468,9 @@ struct ubus_client *ubusd_proto_new_client(int fd, uloop_fd_handler cb) if (!cl) return NULL; + if (ubusd_acl_init_client(cl, fd)) + goto free; + INIT_LIST_HEAD(&cl->objects); cl->sock.fd = fd; cl->sock.cb = cb; @@ -474,6 +500,7 @@ void ubusd_proto_free_client(struct ubus_client *cl) ubusd_free_object(obj); } + ubusd_acl_free_client(cl); ubus_free_id(&clients, &cl->id); } @@ -487,6 +514,9 @@ void ubus_notify_subscription(struct ubus_object *obj) blob_put_int8(&b, UBUS_ATTR_ACTIVE, active); ub = ubus_msg_from_blob(false); + if (!ub) + return; + ubus_msg_init(ub, UBUS_MSG_NOTIFY, ++obj->invoke_seq, 0); ubus_msg_send(obj->client, ub, true); } @@ -500,8 +530,10 @@ void ubus_notify_unsubscribe(struct ubus_subscription *s) blob_put_int32(&b, UBUS_ATTR_TARGET, s->target->id.id); ub = ubus_msg_from_blob(false); - ubus_msg_init(ub, UBUS_MSG_UNSUBSCRIBE, ++s->subscriber->invoke_seq, 0); - ubus_msg_send(s->subscriber->client, ub, true); + if (ub != NULL) { + ubus_msg_init(ub, UBUS_MSG_UNSUBSCRIBE, ++s->subscriber->invoke_seq, 0); + ubus_msg_send(s->subscriber->client, ub, true); + } ubus_unsubscribe(s); } diff --git a/3P/ubus/ubusmsg.h b/3P/ubus/ubusmsg.h index 0a27b42a..398b126b 100644 --- a/3P/ubus/ubusmsg.h +++ b/3P/ubus/ubusmsg.h @@ -22,6 +22,8 @@ #define UBUS_MSG_CHUNK_SIZE 65536 #define UBUS_SYSTEM_OBJECT_EVENT 1 +#define UBUS_SYSTEM_OBJECT_ACL 2 +#define UBUS_SYSTEM_OBJECT_MONITOR 3 #define UBUS_SYSTEM_OBJECT_MAX 1024 struct ubus_msghdr { @@ -68,6 +70,8 @@ enum ubus_msg_type { */ UBUS_MSG_NOTIFY, + UBUS_MSG_MONITOR, + /* must be last */ __UBUS_MSG_LAST, }; @@ -92,10 +96,25 @@ enum ubus_msg_attr { UBUS_ATTR_SUBSCRIBERS, + UBUS_ATTR_USER, + UBUS_ATTR_GROUP, + /* must be last */ UBUS_ATTR_MAX, }; +enum ubus_monitor_attr { + UBUS_MONITOR_CLIENT, + UBUS_MONITOR_PEER, + UBUS_MONITOR_SEND, + UBUS_MONITOR_SEQ, + UBUS_MONITOR_TYPE, + UBUS_MONITOR_DATA, + + /* must be last */ + UBUS_MONITOR_MAX, +}; + enum ubus_msg_status { UBUS_STATUS_OK, UBUS_STATUS_INVALID_COMMAND, diff --git a/CMakeLists.txt b/CMakeLists.txt index 10bfb546..b559c748 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,8 +24,8 @@ add_custom_target (deploy #COMMAND cp ${CMAKE_SOURCE_DIR}/scripts/Domo.sh ${CMAKE_SOURCE_DIR}/build/install COMMAND cp ${CMAKE_SOURCE_DIR}/build/bin/* ${CMAKE_SOURCE_DIR}/build/install/bin/ COMMAND cp ${CMAKE_SOURCE_DIR}/build/lib/*.so ${CMAKE_SOURCE_DIR}/build/install/lib/ - COMMAND cp -a ${CMAKE_SOURCE_DIR}/build/html ${CMAKE_SOURCE_DIR}/build/install/ - COMMAND cp -a ${CMAKE_SOURCE_DIR}/build/rsc ${CMAKE_SOURCE_DIR}/build/install/ + #COMMAND cp -a ${CMAKE_SOURCE_DIR}/build/html ${CMAKE_SOURCE_DIR}/build/install/ + #COMMAND cp -a ${CMAKE_SOURCE_DIR}/build/rsc ${CMAKE_SOURCE_DIR}/build/install/ COMMAND rsync -av --delete ${CMAKE_SOURCE_DIR}/build/install/* root@${PI}:/opt/Domo/ )