2 * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU Lesser General Public License version 2.1
6 * as published by the Free Software Foundation
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
15 #include <sys/socket.h>
16 #include <sys/types.h>
25 #include <libubox/vlist.h>
26 #include <libubox/blobmsg_json.h>
27 #include <libubox/avl-cmp.h>
39 struct ubusd_acl_obj {
41 struct list_head list;
46 struct blob_attr *methods;
47 struct blob_attr *tags;
48 struct blob_attr *priv;
53 struct ubusd_acl_file {
54 struct vlist_node avl;
59 struct blob_attr *blob;
65 static struct blob_buf bbuf;
66 static struct avl_tree ubusd_acls;
67 static int ubusd_acl_seq;
68 static struct ubus_object *acl_obj;
71 ubusd_acl_match_path(const void *k1, const void *k2, void *ptr)
73 const char *name = k1;
74 const char *match = k2;
75 char *wildcard = strstr(match, "\t");
78 return strncmp(name, match, wildcard - match);
80 return strcmp(name, match);
84 ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj)
86 if (obj->user && !strcmp(cl->user, obj->user))
89 if (obj->group && !strcmp(cl->group, obj->group))
96 ubusd_acl_check(struct ubus_client *cl, const char *obj,
97 const char *method, enum ubusd_acl_type type)
99 struct ubusd_acl_obj *acl;
100 struct blob_attr *cur;
103 if (!cl->gid && !cl->uid)
106 acl = avl_find_ge_element(&ubusd_acls, obj, acl, avl);
108 int diff = ubusd_acl_match_path(obj, acl->avl.key, NULL);
113 if (ubusd_acl_match_cred(cl, acl)) {
114 acl = avl_next_element(acl, avl);
119 case UBUS_ACL_PUBLISH:
124 case UBUS_ACL_SUBSCRIBE:
129 case UBUS_ACL_ACCESS:
131 blobmsg_for_each_attr(cur, acl->methods, rem)
132 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
133 if (!ubusd_acl_match_path(method, blobmsg_get_string(cur), NULL))
137 acl = avl_next_element(acl, avl);
144 ubusd_acl_init_client(struct ubus_client *cl, int fd)
151 unsigned int len = sizeof(struct ucred);
153 if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
156 memset(&cred, 0, sizeof(cred));
159 pwd = getpwuid(cred.uid);
163 group = getgrgid(cred.gid);
170 cl->group = strdup(group->gr_name);
171 cl->user = strdup(pwd->pw_name);
177 ubusd_acl_file_free(struct ubusd_acl_file *file)
179 struct ubusd_acl_obj *p, *q;
181 list_for_each_entry_safe(p, q, &file->acl, list) {
182 avl_delete(&ubusd_acls, &p->avl);
197 static const struct blobmsg_policy acl_obj_policy[__ACL_ACCESS_MAX] = {
198 [ACL_ACCESS_METHODS] = { .name = "methods", .type = BLOBMSG_TYPE_ARRAY },
199 [ACL_ACCESS_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY },
200 [ACL_ACCESS_PRIV] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE },
203 static struct ubusd_acl_obj*
204 ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
206 struct ubusd_acl_obj *o;
209 o = calloc_a(sizeof(*o), &k, strlen(obj) + 1);
210 o->user = file->user;
211 o->group = file->group;
221 list_add(&o->list, &file->acl);
222 avl_insert(&ubusd_acls, &o->avl);
228 ubusd_acl_add_access(struct ubusd_acl_file *file, struct blob_attr *obj)
230 struct blob_attr *tb[__ACL_ACCESS_MAX];
231 struct ubusd_acl_obj *o;
233 blobmsg_parse(acl_obj_policy, __ACL_ACCESS_MAX, tb, blobmsg_data(obj),
234 blobmsg_data_len(obj));
236 if (!tb[ACL_ACCESS_METHODS] && !tb[ACL_ACCESS_TAGS] && !tb[ACL_ACCESS_PRIV])
239 o = ubusd_acl_alloc_obj(file, blobmsg_name(obj));
241 o->methods = tb[ACL_ACCESS_METHODS];
242 o->tags = tb[ACL_ACCESS_TAGS];
243 o->priv = tb[ACL_ACCESS_PRIV];
245 if (file->user || file->group)
250 ubusd_acl_add_subscribe(struct ubusd_acl_file *file, const char *obj)
252 struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
258 ubusd_acl_add_publish(struct ubusd_acl_file *file, const char *obj)
260 struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
275 static const struct blobmsg_policy acl_policy[__ACL_MAX] = {
276 [ACL_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING },
277 [ACL_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING },
278 [ACL_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_TABLE },
279 [ACL_PUBLISH] = { .name = "publish", .type = BLOBMSG_TYPE_ARRAY },
280 [ACL_SUBSCRIBE] = { .name = "subscribe", .type = BLOBMSG_TYPE_ARRAY },
281 [ACL_INHERIT] = { .name = "inherit", .type = BLOBMSG_TYPE_ARRAY },
285 ubusd_acl_file_add(struct ubusd_acl_file *file)
287 struct blob_attr *tb[__ACL_MAX], *cur;
290 blobmsg_parse(acl_policy, __ACL_MAX, tb, blob_data(file->blob),
291 blob_len(file->blob));
294 file->user = blobmsg_get_string(tb[ACL_USER]);
295 else if (tb[ACL_GROUP])
296 file->group = blobmsg_get_string(tb[ACL_GROUP]);
301 blobmsg_for_each_attr(cur, tb[ACL_ACCESS], rem)
302 ubusd_acl_add_access(file, cur);
304 if (tb[ACL_SUBSCRIBE])
305 blobmsg_for_each_attr(cur, tb[ACL_SUBSCRIBE], rem)
306 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
307 ubusd_acl_add_subscribe(file, blobmsg_get_string(cur));
310 blobmsg_for_each_attr(cur, tb[ACL_PUBLISH], rem)
311 if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
312 ubusd_acl_add_publish(file, blobmsg_get_string(cur));
316 ubusd_acl_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
317 struct vlist_node *node_old)
319 struct ubusd_acl_file *file;
322 file = container_of(node_old, struct ubusd_acl_file, avl);
323 ubusd_acl_file_free(file);
327 file = container_of(node_new, struct ubusd_acl_file, avl);
328 ubusd_acl_file_add(file);
332 static struct ubus_msg_buf *
333 ubusd_create_sequence_event_msg(void *priv, const char *id)
337 blob_buf_init(&b, 0);
338 blob_put_int32(&b, UBUS_ATTR_OBJID, 0);
339 blob_put_string(&b, UBUS_ATTR_METHOD, id);
340 s = blob_nest_start(&b, UBUS_ATTR_DATA);
341 blobmsg_add_u32(&b, "sequence", ubusd_acl_seq);
342 blob_nest_end(&b, s);
344 return ubus_msg_new(b.head, blob_raw_len(b.head), true);
347 static VLIST_TREE(ubusd_acl_files, avl_strcmp, ubusd_acl_update_cb, false, false);
350 ubusd_acl_load_file(const char *filename)
352 struct ubusd_acl_file *file;
355 blob_buf_init(&bbuf, 0);
356 if (!blobmsg_add_json_from_file(&bbuf, filename)) {
357 syslog(LOG_ERR, "failed to parse %s\n", filename);
361 file = calloc_a(sizeof(*file), &blob, blob_raw_len(bbuf.head));
367 memcpy(blob, bbuf.head, blob_raw_len(bbuf.head));
368 INIT_LIST_HEAD(&file->acl);
370 vlist_add(&ubusd_acl_files, &file->avl, filename);
371 syslog(LOG_INFO, "loading %s\n", filename);
383 if (glob("/usr/share/acl.d/*.json", GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
386 vlist_update(&ubusd_acl_files);
387 for (j = 0; j < gl.gl_pathc; j++) {
388 if (stat(gl.gl_pathv[j], &st) || !S_ISREG(st.st_mode))
391 if (st.st_uid || st.st_gid) {
392 syslog(LOG_ERR, "%s has wrong owner\n", gl.gl_pathv[j]);
395 if (st.st_mode & (S_IWOTH | S_IWGRP | S_IXOTH)) {
396 syslog(LOG_ERR, "%s has wrong permissions\n", gl.gl_pathv[j]);
399 ubusd_acl_load_file(gl.gl_pathv[j]);
403 vlist_flush(&ubusd_acl_files);
405 ubusd_send_event(NULL, "ubus.acl.sequence", ubusd_create_sequence_event_msg, NULL);
409 ubusd_reply_add(struct ubus_object *obj)
411 struct ubusd_acl_obj *acl;
415 acl = avl_find_ge_element(&ubusd_acls, obj->path.key, acl, avl);
416 while (acl && !avl_is_last(&ubusd_acls, &acl->avl) &&
417 !ubusd_acl_match_path(obj->path.key, acl->avl.key, NULL)) {
420 void *c = blobmsg_open_table(&b, NULL);
422 blobmsg_add_string(&b, "obj", obj->path.key);
424 blobmsg_add_string(&b, "user", acl->user);
426 blobmsg_add_string(&b, "group", acl->group);
429 blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl",
430 blobmsg_data(acl->priv), blobmsg_data_len(acl->priv));
432 blobmsg_close_table(&b, c);
434 acl = avl_next_element(acl, avl);
437 static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr, struct blob_attr *msg)
439 struct ubus_object *obj;
442 if (!attr[UBUS_ATTR_OBJID])
443 return UBUS_STATUS_INVALID_ARGUMENT;
445 obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID]));
447 return UBUS_STATUS_NOT_FOUND;
449 blob_buf_init(&b, 0);
450 blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
451 d = blob_nest_start(&b, UBUS_ATTR_DATA);
453 blobmsg_add_u32(&b, "seq", ubusd_acl_seq);
454 a = blobmsg_open_array(&b, "acl");
455 list_for_each_entry(obj, &cl->objects, list)
456 ubusd_reply_add(obj);
457 blobmsg_close_table(&b, a);
459 blob_nest_end(&b, d);
461 ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA);
466 static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg)
468 if (!strcmp(method, "query"))
469 return ubusd_reply_query(cl, ub, ubus_parse_msg(ub->data), msg);
471 return UBUS_STATUS_INVALID_COMMAND;
474 void ubusd_acl_init(void)
476 avl_init(&ubusd_acls, ubusd_acl_match_path, true, NULL);
477 acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL);
478 acl_obj->recv_msg = ubusd_acl_recv;