2 * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
3 * Copyright (C) 2018 Hans Dedecker <dedeckeh@gmail.com>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License version 2.1
7 * as published by the Free Software Foundation
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
16 #include <sys/socket.h>
17 #include <sys/types.h>
26 #include <libubox/vlist.h>
27 #include <libubox/blobmsg_json.h>
28 #include <libubox/avl-cmp.h>
40 struct ubusd_acl_obj {
42 struct list_head list;
49 struct blob_attr *methods;
50 struct blob_attr *tags;
51 struct blob_attr *priv;
58 struct ubusd_acl_file {
59 struct vlist_node avl;
64 struct blob_attr *blob;
70 const char *ubusd_acl_dir = "/usr/share/acl.d";
71 static struct blob_buf bbuf;
72 static struct avl_tree ubusd_acls;
73 static int ubusd_acl_seq;
74 static struct ubus_object *acl_obj;
77 ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj)
79 if (obj->user && !strcmp(cl->user, obj->user))
82 if (obj->group && !strcmp(cl->group, obj->group))
89 ubusd_acl_check(struct ubus_client *cl, const char *obj,
90 const char *method, enum ubusd_acl_type type)
92 struct ubusd_acl_obj *acl;
95 if (!cl || !cl->uid || !obj)
99 * Since this tree is sorted alphabetically, we can only expect
100 * to find matching entries as long as the number of matching
101 * characters between the access list string and the object path
102 * is monotonically increasing.
104 avl_for_each_element(&ubusd_acls, acl, avl) {
105 const char *key = acl->avl.key;
109 full_match = ubus_strmatch_len(obj, key, &cur_match_len);
110 if (cur_match_len < match_len)
113 match_len = cur_match_len;
119 if (match_len != strlen(key))
123 if (ubusd_acl_match_cred(cl, acl))
127 case UBUS_ACL_PUBLISH:
132 case UBUS_ACL_SUBSCRIBE:
137 case UBUS_ACL_LISTEN:
147 case UBUS_ACL_ACCESS:
149 struct blob_attr *cur;
152 blobmsg_for_each_attr(cur, acl->methods, rem)
153 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
154 if (!strcmp(method, blobmsg_get_string(cur)))
165 ubusd_acl_init_client(struct ubus_client *cl, int fd)
172 unsigned int len = sizeof(struct ucred);
174 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
177 memset(&cred, 0, sizeof(cred));
180 pwd = getpwuid(cred.uid);
184 group = getgrgid(cred.gid);
191 cl->group = strdup(group->gr_name);
192 cl->user = strdup(pwd->pw_name);
198 ubusd_acl_free_client(struct ubus_client *cl)
205 ubusd_acl_file_free(struct ubusd_acl_file *file)
207 struct ubusd_acl_obj *p, *q;
209 list_for_each_entry_safe(p, q, &file->acl, list) {
210 avl_delete(&ubusd_acls, &p->avl);
225 static const struct blobmsg_policy acl_obj_policy[__ACL_ACCESS_MAX] = {
226 [ACL_ACCESS_METHODS] = { .name = "methods", .type = BLOBMSG_TYPE_ARRAY },
227 [ACL_ACCESS_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY },
228 [ACL_ACCESS_PRIV] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE },
231 static struct ubusd_acl_obj*
232 ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
234 struct ubusd_acl_obj *o;
235 int len = strlen(obj);
237 bool partial = false;
239 if (obj[len - 1] == '*') {
244 o = calloc_a(sizeof(*o), &k, len + 1);
245 o->partial = partial;
246 o->user = file->user;
247 o->group = file->group;
248 o->avl.key = memcpy(k, obj, len);
250 list_add(&o->list, &file->acl);
251 avl_insert(&ubusd_acls, &o->avl);
257 ubusd_acl_add_access(struct ubusd_acl_file *file, struct blob_attr *obj)
259 struct blob_attr *tb[__ACL_ACCESS_MAX];
260 struct ubusd_acl_obj *o;
262 blobmsg_parse(acl_obj_policy, __ACL_ACCESS_MAX, tb, blobmsg_data(obj),
263 blobmsg_data_len(obj));
265 if (!tb[ACL_ACCESS_METHODS] && !tb[ACL_ACCESS_TAGS] && !tb[ACL_ACCESS_PRIV])
268 o = ubusd_acl_alloc_obj(file, blobmsg_name(obj));
270 o->methods = tb[ACL_ACCESS_METHODS];
271 o->tags = tb[ACL_ACCESS_TAGS];
272 o->priv = tb[ACL_ACCESS_PRIV];
274 if (file->user || file->group)
279 ubusd_acl_add_subscribe(struct ubusd_acl_file *file, const char *obj)
281 struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
287 ubusd_acl_add_publish(struct ubusd_acl_file *file, const char *obj)
289 struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
294 static void ubusd_acl_add_listen(struct ubusd_acl_file *file, const char *obj)
296 struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
301 static void ubusd_acl_add_send(struct ubusd_acl_file *file, const char *obj)
303 struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
320 static const struct blobmsg_policy acl_policy[__ACL_MAX] = {
321 [ACL_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING },
322 [ACL_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING },
323 [ACL_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_TABLE },
324 [ACL_PUBLISH] = { .name = "publish", .type = BLOBMSG_TYPE_ARRAY },
325 [ACL_SUBSCRIBE] = { .name = "subscribe", .type = BLOBMSG_TYPE_ARRAY },
326 [ACL_INHERIT] = { .name = "inherit", .type = BLOBMSG_TYPE_ARRAY },
327 [ACL_LISTEN] = { .name= "listen", .type = BLOBMSG_TYPE_ARRAY },
328 [ACL_SEND] = { .name= "send", .type = BLOBMSG_TYPE_ARRAY },
332 ubusd_acl_file_add(struct ubusd_acl_file *file)
334 struct blob_attr *tb[__ACL_MAX], *cur;
337 blobmsg_parse(acl_policy, __ACL_MAX, tb, blob_data(file->blob),
338 blob_len(file->blob));
341 file->user = blobmsg_get_string(tb[ACL_USER]);
342 else if (tb[ACL_GROUP])
343 file->group = blobmsg_get_string(tb[ACL_GROUP]);
348 blobmsg_for_each_attr(cur, tb[ACL_ACCESS], rem)
349 ubusd_acl_add_access(file, cur);
351 if (tb[ACL_SUBSCRIBE])
352 blobmsg_for_each_attr(cur, tb[ACL_SUBSCRIBE], rem)
353 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
354 ubusd_acl_add_subscribe(file, blobmsg_get_string(cur));
357 blobmsg_for_each_attr(cur, tb[ACL_PUBLISH], rem)
358 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
359 ubusd_acl_add_publish(file, blobmsg_get_string(cur));
362 blobmsg_for_each_attr(cur, tb[ACL_LISTEN], rem)
363 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
364 ubusd_acl_add_listen(file, blobmsg_get_string(cur));
367 blobmsg_for_each_attr(cur, tb[ACL_SEND], rem)
368 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
369 ubusd_acl_add_send(file, blobmsg_get_string(cur));
373 ubusd_acl_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
374 struct vlist_node *node_old)
376 struct ubusd_acl_file *file;
379 file = container_of(node_old, struct ubusd_acl_file, avl);
380 ubusd_acl_file_free(file);
384 file = container_of(node_new, struct ubusd_acl_file, avl);
385 ubusd_acl_file_add(file);
389 static struct ubus_msg_buf *
390 ubusd_create_sequence_event_msg(void *priv, const char *id)
394 blob_buf_init(&b, 0);
395 blob_put_int32(&b, UBUS_ATTR_OBJID, 0);
396 blob_put_string(&b, UBUS_ATTR_METHOD, id);
397 s = blob_nest_start(&b, UBUS_ATTR_DATA);
398 blobmsg_add_u32(&b, "sequence", ubusd_acl_seq);
399 blob_nest_end(&b, s);
401 return ubus_msg_new(b.head, blob_raw_len(b.head), true);
404 static VLIST_TREE(ubusd_acl_files, avl_strcmp, ubusd_acl_update_cb, false, false);
407 ubusd_acl_load_file(const char *filename)
409 struct ubusd_acl_file *file;
412 blob_buf_init(&bbuf, 0);
413 if (!blobmsg_add_json_from_file(&bbuf, filename)) {
414 syslog(LOG_ERR, "failed to parse %s\n", filename);
418 file = calloc_a(sizeof(*file), &blob, blob_raw_len(bbuf.head));
424 memcpy(blob, bbuf.head, blob_raw_len(bbuf.head));
425 INIT_LIST_HEAD(&file->acl);
427 vlist_add(&ubusd_acl_files, &file->avl, filename);
428 syslog(LOG_INFO, "loading %s\n", filename);
439 const char *suffix = "/*.json";
440 char *path = alloca(strlen(ubusd_acl_dir) + strlen(suffix) + 1);
442 sprintf(path, "%s%s", ubusd_acl_dir, suffix);
443 if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
446 vlist_update(&ubusd_acl_files);
447 for (j = 0; j < gl.gl_pathc; j++) {
448 if (stat(gl.gl_pathv[j], &st) || !S_ISREG(st.st_mode))
451 if (st.st_uid || st.st_gid) {
452 syslog(LOG_ERR, "%s has wrong owner\n", gl.gl_pathv[j]);
455 if (st.st_mode & (S_IWOTH | S_IWGRP | S_IXOTH)) {
456 syslog(LOG_ERR, "%s has wrong permissions\n", gl.gl_pathv[j]);
459 ubusd_acl_load_file(gl.gl_pathv[j]);
463 vlist_flush(&ubusd_acl_files);
465 ubusd_send_event(NULL, "ubus.acl.sequence", ubusd_create_sequence_event_msg, NULL);
469 ubusd_reply_add(struct ubus_object *obj)
471 struct ubusd_acl_obj *acl;
478 * Since this tree is sorted alphabetically, we can only expect
479 * to find matching entries as long as the number of matching
480 * characters between the access list string and the object path
481 * is monotonically increasing.
483 avl_for_each_element(&ubusd_acls, acl, avl) {
484 const char *key = acl->avl.key;
492 full_match = ubus_strmatch_len(obj->path.key, key, &cur_match_len);
493 if (cur_match_len < match_len)
496 match_len = cur_match_len;
502 if (match_len != strlen(key))
506 c = blobmsg_open_table(&b, NULL);
507 blobmsg_add_string(&b, "obj", obj->path.key);
509 blobmsg_add_string(&b, "user", acl->user);
511 blobmsg_add_string(&b, "group", acl->group);
513 blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl",
514 blobmsg_data(acl->priv), blobmsg_data_len(acl->priv));
516 blobmsg_close_table(&b, c);
520 static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr, struct blob_attr *msg)
522 struct ubus_object *obj;
525 if (!attr[UBUS_ATTR_OBJID])
526 return UBUS_STATUS_INVALID_ARGUMENT;
528 obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID]));
530 return UBUS_STATUS_NOT_FOUND;
532 blob_buf_init(&b, 0);
533 blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
534 d = blob_nest_start(&b, UBUS_ATTR_DATA);
536 blobmsg_add_u32(&b, "seq", ubusd_acl_seq);
537 a = blobmsg_open_array(&b, "acl");
538 list_for_each_entry(obj, &cl->objects, list)
539 ubusd_reply_add(obj);
540 blobmsg_close_table(&b, a);
542 blob_nest_end(&b, d);
544 ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA);
549 static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg)
551 if (!strcmp(method, "query"))
552 return ubusd_reply_query(cl, ub, ubus_parse_msg(ub->data), msg);
554 return UBUS_STATUS_INVALID_COMMAND;
557 void ubusd_acl_init(void)
559 ubus_init_string_tree(&ubusd_acls, true);
560 acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL);
561 acl_obj->recv_msg = ubusd_acl_recv;