2 * luci-rpcd - LuCI UBUS RPC server
4 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
5 * Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20 #include <libubox/avl-cmp.h>
21 #include <libubox/utils.h>
27 static struct avl_tree sessions;
28 static struct blob_buf buf;
30 static const struct blobmsg_policy new_policy = {
31 .name = "timeout", .type = BLOBMSG_TYPE_INT32
34 static const struct blobmsg_policy sid_policy = {
35 .name = "sid", .type = BLOBMSG_TYPE_STRING
43 static const struct blobmsg_policy set_policy[__RPC_SS_MAX] = {
44 [RPC_SS_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
45 [RPC_SS_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE },
53 static const struct blobmsg_policy get_policy[__RPC_SG_MAX] = {
54 [RPC_SG_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
55 [RPC_SG_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY },
64 static const struct blobmsg_policy acl_policy[__RPC_SA_MAX] = {
65 [RPC_SA_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
66 [RPC_SA_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
67 [RPC_SA_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY },
77 static const struct blobmsg_policy perm_policy[__RPC_SP_MAX] = {
78 [RPC_SP_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
79 [RPC_SP_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
80 [RPC_SP_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_STRING },
81 [RPC_SP_FUNCTION] = { .name = "function", .type = BLOBMSG_TYPE_STRING },
85 * Keys in the AVL tree contain all pattern characters up to the first wildcard.
86 * To look up entries, start with the last entry that has a key less than or
87 * equal to the method name, then work backwards as long as the AVL key still
88 * matches its counterpart in the object name
90 #define uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \
91 for (_acl = avl_find_le_element(_avl, _obj, _acl, avl); \
93 _acl = avl_is_first(_avl, &(_acl)->avl) ? NULL : \
94 avl_prev_element((_acl), avl))
96 #define uh_foreach_matching_acl(_acl, _avl, _obj, _func) \
97 uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \
98 if (!strncmp((_acl)->object, _obj, (_acl)->sort_len) && \
99 !fnmatch((_acl)->object, (_obj), FNM_NOESCAPE) && \
100 !fnmatch((_acl)->function, (_func), FNM_NOESCAPE))
103 rpc_random(char *dest)
105 unsigned char buf[16] = { 0 };
109 f = fopen("/dev/urandom", "r");
113 fread(buf, 1, sizeof(buf), f);
116 for (i = 0; i < sizeof(buf); i++)
117 sprintf(dest + (i<<1), "%02x", buf[i]);
121 rpc_session_dump_data(struct rpc_session *ses, struct blob_buf *b)
123 struct rpc_session_data *d;
125 avl_for_each_element(&ses->data, d, avl) {
126 blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr),
127 blobmsg_data(d->attr), blobmsg_data_len(d->attr));
132 rpc_session_dump_acls(struct rpc_session *ses, struct blob_buf *b)
134 struct rpc_session_acl *acl;
135 struct rpc_session_acl_scope *acl_scope;
136 const char *lastobj = NULL;
137 const char *lastscope = NULL;
138 void *c = NULL, *d = NULL;
140 avl_for_each_element(&ses->acls, acl_scope, avl) {
141 if (!lastscope || strcmp(acl_scope->avl.key, lastscope))
143 if (c) blobmsg_close_table(b, c);
144 c = blobmsg_open_table(b, acl_scope->avl.key);
149 avl_for_each_element(&acl_scope->acls, acl, avl) {
150 if (!lastobj || strcmp(acl->object, lastobj))
152 if (d) blobmsg_close_array(b, d);
153 d = blobmsg_open_array(b, acl->object);
156 blobmsg_add_string(b, NULL, acl->function);
157 lastobj = acl->object;
160 if (d) blobmsg_close_array(b, d);
163 if (c) blobmsg_close_table(b, c);
167 rpc_session_dump(struct rpc_session *ses,
168 struct ubus_context *ctx,
169 struct ubus_request_data *req)
173 blob_buf_init(&buf, 0);
175 blobmsg_add_string(&buf, "sid", ses->id);
176 blobmsg_add_u32(&buf, "timeout", ses->timeout);
177 blobmsg_add_u32(&buf, "expires", uloop_timeout_remaining(&ses->t) / 1000);
179 c = blobmsg_open_table(&buf, "acls");
180 rpc_session_dump_acls(ses, &buf);
181 blobmsg_close_table(&buf, c);
183 c = blobmsg_open_table(&buf, "data");
184 rpc_session_dump_data(ses, &buf);
185 blobmsg_close_table(&buf, c);
187 ubus_send_reply(ctx, req, buf.head);
191 rpc_touch_session(struct rpc_session *ses)
193 uloop_timeout_set(&ses->t, ses->timeout * 1000);
197 rpc_session_destroy(struct rpc_session *ses)
199 struct rpc_session_acl *acl, *nacl;
200 struct rpc_session_acl_scope *acl_scope, *nacl_scope;
201 struct rpc_session_data *data, *ndata;
203 uloop_timeout_cancel(&ses->t);
205 avl_for_each_element_safe(&ses->acls, acl_scope, avl, nacl_scope) {
206 avl_remove_all_elements(&acl_scope->acls, acl, avl, nacl)
209 avl_delete(&ses->acls, &acl_scope->avl);
213 avl_remove_all_elements(&ses->data, data, avl, ndata)
216 avl_delete(&sessions, &ses->avl);
220 static void rpc_session_timeout(struct uloop_timeout *t)
222 struct rpc_session *ses;
224 ses = container_of(t, struct rpc_session, t);
225 rpc_session_destroy(ses);
228 static struct rpc_session *
229 rpc_session_create(int timeout)
231 struct rpc_session *ses;
233 ses = calloc(1, sizeof(*ses));
237 ses->timeout = timeout;
238 ses->avl.key = ses->id;
241 avl_insert(&sessions, &ses->avl);
242 avl_init(&ses->acls, avl_strcmp, true, NULL);
243 avl_init(&ses->data, avl_strcmp, false, NULL);
245 ses->t.cb = rpc_session_timeout;
246 rpc_touch_session(ses);
251 static struct rpc_session *
252 rpc_session_get(const char *id)
254 struct rpc_session *ses;
256 ses = avl_find_element(&sessions, id, ses, avl);
260 rpc_touch_session(ses);
265 rpc_handle_create(struct ubus_context *ctx, struct ubus_object *obj,
266 struct ubus_request_data *req, const char *method,
267 struct blob_attr *msg)
269 struct rpc_session *ses;
270 struct blob_attr *tb;
271 int timeout = RPC_DEFAULT_SESSION_TIMEOUT;
273 blobmsg_parse(&new_policy, 1, &tb, blob_data(msg), blob_len(msg));
275 timeout = blobmsg_get_u32(tb);
277 ses = rpc_session_create(timeout);
279 rpc_session_dump(ses, ctx, req);
285 rpc_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
286 struct ubus_request_data *req, const char *method,
287 struct blob_attr *msg)
289 struct rpc_session *ses;
290 struct blob_attr *tb;
292 blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
295 avl_for_each_element(&sessions, ses, avl)
296 rpc_session_dump(ses, ctx, req);
300 ses = rpc_session_get(blobmsg_data(tb));
302 return UBUS_STATUS_NOT_FOUND;
304 rpc_session_dump(ses, ctx, req);
310 uh_id_len(const char *str)
312 return strcspn(str, "*?[");
316 rpc_session_grant(struct rpc_session *ses, struct ubus_context *ctx,
317 const char *scope, const char *object, const char *function)
319 struct rpc_session_acl *acl;
320 struct rpc_session_acl_scope *acl_scope;
321 char *new_scope, *new_obj, *new_func, *new_id;
324 if (!object || !function)
325 return UBUS_STATUS_INVALID_ARGUMENT;
327 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
330 uh_foreach_matching_acl_prefix(acl, &acl_scope->acls, object, function) {
331 if (!strcmp(acl->object, object) &&
332 !strcmp(acl->function, function))
338 acl_scope = calloc_a(sizeof(*acl_scope),
339 &new_scope, strlen(scope) + 1);
342 return UBUS_STATUS_UNKNOWN_ERROR;
344 acl_scope->avl.key = strcpy(new_scope, scope);
345 avl_init(&acl_scope->acls, avl_strcmp, true, NULL);
346 avl_insert(&ses->acls, &acl_scope->avl);
349 id_len = uh_id_len(object);
350 acl = calloc_a(sizeof(*acl),
351 &new_obj, strlen(object) + 1,
352 &new_func, strlen(function) + 1,
353 &new_id, id_len + 1);
356 return UBUS_STATUS_UNKNOWN_ERROR;
358 acl->object = strcpy(new_obj, object);
359 acl->function = strcpy(new_func, function);
360 acl->avl.key = strncpy(new_id, object, id_len);
361 avl_insert(&acl_scope->acls, &acl->avl);
367 rpc_session_revoke(struct rpc_session *ses, struct ubus_context *ctx,
368 const char *scope, const char *object, const char *function)
370 struct rpc_session_acl *acl, *next;
371 struct rpc_session_acl_scope *acl_scope;
375 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
380 if (!object && !function) {
381 avl_remove_all_elements(&acl_scope->acls, acl, avl, next)
383 avl_delete(&ses->acls, &acl_scope->avl);
388 id_len = uh_id_len(object);
389 id = alloca(id_len + 1);
390 strncpy(id, object, id_len);
393 acl = avl_find_element(&acl_scope->acls, id, acl, avl);
395 if (!avl_is_last(&acl_scope->acls, &acl->avl))
396 next = avl_next_element(acl, avl);
400 if (strcmp(id, acl->avl.key) != 0)
403 if (!strcmp(acl->object, object) &&
404 !strcmp(acl->function, function)) {
405 avl_delete(&acl_scope->acls, &acl->avl);
411 if (avl_is_empty(&acl_scope->acls)) {
412 avl_delete(&ses->acls, &acl_scope->avl);
421 rpc_handle_acl(struct ubus_context *ctx, struct ubus_object *obj,
422 struct ubus_request_data *req, const char *method,
423 struct blob_attr *msg)
425 struct rpc_session *ses;
426 struct blob_attr *tb[__RPC_SA_MAX];
427 struct blob_attr *attr, *sattr;
428 const char *object, *function;
429 const char *scope = "ubus";
432 int (*cb)(struct rpc_session *ses, struct ubus_context *ctx,
433 const char *scope, const char *object, const char *function);
435 blobmsg_parse(acl_policy, __RPC_SA_MAX, tb, blob_data(msg), blob_len(msg));
438 return UBUS_STATUS_INVALID_ARGUMENT;
440 ses = rpc_session_get(blobmsg_data(tb[RPC_SA_SID]));
442 return UBUS_STATUS_NOT_FOUND;
444 if (tb[RPC_SA_SCOPE])
445 scope = blobmsg_data(tb[RPC_SA_SCOPE]);
447 if (!strcmp(method, "grant"))
448 cb = rpc_session_grant;
450 cb = rpc_session_revoke;
452 if (!tb[RPC_SA_OBJECTS])
453 return cb(ses, ctx, scope, NULL, NULL);
455 blobmsg_for_each_attr(attr, tb[RPC_SA_OBJECTS], rem1) {
456 if (blob_id(attr) != BLOBMSG_TYPE_ARRAY)
462 blobmsg_for_each_attr(sattr, attr, rem2) {
463 if (blob_id(sattr) != BLOBMSG_TYPE_STRING)
467 object = blobmsg_data(sattr);
469 function = blobmsg_data(sattr);
474 if (object && function)
475 cb(ses, ctx, scope, object, function);
482 rpc_session_acl_allowed(struct rpc_session *ses, const char *scope,
483 const char *obj, const char *fun)
485 struct rpc_session_acl *acl;
486 struct rpc_session_acl_scope *acl_scope;
488 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
491 uh_foreach_matching_acl(acl, &acl_scope->acls, obj, fun)
499 rpc_handle_access(struct ubus_context *ctx, struct ubus_object *obj,
500 struct ubus_request_data *req, const char *method,
501 struct blob_attr *msg)
503 struct rpc_session *ses;
504 struct blob_attr *tb[__RPC_SP_MAX];
505 const char *scope = "ubus";
508 blobmsg_parse(perm_policy, __RPC_SP_MAX, tb, blob_data(msg), blob_len(msg));
510 if (!tb[RPC_SP_SID] || !tb[RPC_SP_OBJECT] || !tb[RPC_SP_FUNCTION])
511 return UBUS_STATUS_INVALID_ARGUMENT;
513 ses = rpc_session_get(blobmsg_data(tb[RPC_SP_SID]));
515 return UBUS_STATUS_NOT_FOUND;
517 if (tb[RPC_SP_SCOPE])
518 scope = blobmsg_data(tb[RPC_SP_SCOPE]);
520 allow = rpc_session_acl_allowed(ses, scope,
521 blobmsg_data(tb[RPC_SP_OBJECT]),
522 blobmsg_data(tb[RPC_SP_FUNCTION]));
524 blob_buf_init(&buf, 0);
525 blobmsg_add_u8(&buf, "access", allow);
526 ubus_send_reply(ctx, req, buf.head);
532 rpc_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
533 struct ubus_request_data *req, const char *method,
534 struct blob_attr *msg)
536 struct rpc_session *ses;
537 struct rpc_session_data *data;
538 struct blob_attr *tb[__RPC_SA_MAX];
539 struct blob_attr *attr;
542 blobmsg_parse(set_policy, __RPC_SS_MAX, tb, blob_data(msg), blob_len(msg));
544 if (!tb[RPC_SS_SID] || !tb[RPC_SS_VALUES])
545 return UBUS_STATUS_INVALID_ARGUMENT;
547 ses = rpc_session_get(blobmsg_data(tb[RPC_SS_SID]));
549 return UBUS_STATUS_NOT_FOUND;
551 blobmsg_for_each_attr(attr, tb[RPC_SS_VALUES], rem) {
552 if (!blobmsg_name(attr)[0])
555 data = avl_find_element(&ses->data, blobmsg_name(attr), data, avl);
557 avl_delete(&ses->data, &data->avl);
561 data = calloc(1, sizeof(*data) + blob_pad_len(attr));
565 memcpy(data->attr, attr, blob_pad_len(attr));
566 data->avl.key = blobmsg_name(data->attr);
567 avl_insert(&ses->data, &data->avl);
574 rpc_handle_get(struct ubus_context *ctx, struct ubus_object *obj,
575 struct ubus_request_data *req, const char *method,
576 struct blob_attr *msg)
578 struct rpc_session *ses;
579 struct rpc_session_data *data;
580 struct blob_attr *tb[__RPC_SA_MAX];
581 struct blob_attr *attr;
585 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
588 return UBUS_STATUS_INVALID_ARGUMENT;
590 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
592 return UBUS_STATUS_NOT_FOUND;
594 blob_buf_init(&buf, 0);
595 c = blobmsg_open_table(&buf, "values");
598 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
599 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
602 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
606 blobmsg_add_field(&buf, blobmsg_type(data->attr),
607 blobmsg_name(data->attr),
608 blobmsg_data(data->attr),
609 blobmsg_data_len(data->attr));
612 rpc_session_dump_data(ses, &buf);
614 blobmsg_close_table(&buf, c);
615 ubus_send_reply(ctx, req, buf.head);
621 rpc_handle_unset(struct ubus_context *ctx, struct ubus_object *obj,
622 struct ubus_request_data *req, const char *method,
623 struct blob_attr *msg)
625 struct rpc_session *ses;
626 struct rpc_session_data *data, *ndata;
627 struct blob_attr *tb[__RPC_SA_MAX];
628 struct blob_attr *attr;
631 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
634 return UBUS_STATUS_INVALID_ARGUMENT;
636 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
638 return UBUS_STATUS_NOT_FOUND;
640 if (!tb[RPC_SG_KEYS]) {
641 avl_remove_all_elements(&ses->data, data, avl, ndata)
646 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
647 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
650 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
654 avl_delete(&ses->data, &data->avl);
662 rpc_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj,
663 struct ubus_request_data *req, const char *method,
664 struct blob_attr *msg)
666 struct rpc_session *ses;
667 struct blob_attr *tb;
669 blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
672 return UBUS_STATUS_INVALID_ARGUMENT;
674 ses = rpc_session_get(blobmsg_data(tb));
676 return UBUS_STATUS_NOT_FOUND;
678 rpc_session_destroy(ses);
683 int rpc_session_api_init(struct ubus_context *ctx)
685 static const struct ubus_method session_methods[] = {
686 UBUS_METHOD("create", rpc_handle_create, &new_policy),
687 UBUS_METHOD("list", rpc_handle_list, &sid_policy),
688 UBUS_METHOD("grant", rpc_handle_acl, acl_policy),
689 UBUS_METHOD("revoke", rpc_handle_acl, acl_policy),
690 UBUS_METHOD("access", rpc_handle_access, perm_policy),
691 UBUS_METHOD("set", rpc_handle_set, set_policy),
692 UBUS_METHOD("get", rpc_handle_get, get_policy),
693 UBUS_METHOD("unset", rpc_handle_unset, get_policy),
694 UBUS_METHOD("destroy", rpc_handle_destroy, &sid_policy),
697 static struct ubus_object_type session_type =
698 UBUS_OBJECT_TYPE("luci-rpc-session", session_methods);
700 static struct ubus_object obj = {
702 .type = &session_type,
703 .methods = session_methods,
704 .n_methods = ARRAY_SIZE(session_methods),
707 avl_init(&sessions, avl_strcmp, false, NULL);
709 return ubus_add_object(ctx, &obj);