X-Git-Url: http://git.openwrt.org/?p=project%2Fubus.git;a=blobdiff_plain;f=ubusd_acl.c;h=4b72663d25aa983cb65b10fae8ba029b099c7c45;hp=491b233427abab7cfa1b4be495b7b2a77990e0b6;hb=HEAD;hpb=33b2abf6310abbadc541a0e1913080e048174324 diff --git a/ubusd_acl.c b/ubusd_acl.c index 491b233..352c581 100644 --- a/ubusd_acl.c +++ b/ubusd_acl.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2015 John Crispin + * Copyright (C) 2018 Hans Dedecker * * 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 @@ -25,6 +26,7 @@ #include #include #include +#include #include "ubusd.h" @@ -40,6 +42,8 @@ struct ubusd_acl_obj { struct avl_node avl; struct list_head list; + bool partial; + const char *user; const char *group; @@ -48,6 +52,8 @@ struct ubusd_acl_obj { struct blob_attr *priv; bool subscribe; bool publish; + bool listen; + bool send; }; struct ubusd_acl_file { @@ -62,24 +68,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 +91,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 +135,32 @@ 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; + char *cur_method; + 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 (blobmsg_type(cur) == BLOBMSG_TYPE_STRING) { + cur_method = blobmsg_get_string(cur); + + if (!strcmp(method, cur_method) || !strcmp("*", cur_method)) return 0; + } + } break; } - acl = avl_next_element(acl, avl); } return -1; @@ -150,19 +176,25 @@ ubusd_acl_init_client(struct ubus_client *cl, int fd) #ifdef SO_PEERCRED unsigned int len = sizeof(struct ucred); - if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) + if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1) { + ULOG_ERR("Failed getsockopt(): %m\n"); return -1; + } #else memset(&cred, 0, sizeof(cred)); #endif pwd = getpwuid(cred.uid); - if (!pwd) + if (!pwd) { + ULOG_ERR("Failed getpwuid(): %m\n"); return -1; + } group = getgrgid(cred.gid); - if (!group) + if (!group) { + ULOG_ERR("Failed getgrgid(): %m\n"); return -1; + } cl->uid = cred.uid; cl->gid = cred.gid; @@ -173,6 +205,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 +243,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; - 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->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 +302,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 +323,8 @@ enum { ACL_PUBLISH, ACL_SUBSCRIBE, ACL_INHERIT, + ACL_LISTEN, + ACL_SEND, __ACL_MAX }; @@ -279,13 +335,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 +355,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 +368,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 +446,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 +480,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; - 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); + 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 (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; @@ -469,14 +560,14 @@ static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, st 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 ubusd_reply_query(cl, ub, ubus_parse_msg(ub->data, blob_raw_len(ub->data)), msg); return UBUS_STATUS_INVALID_COMMAND; } 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; }