2 * rpcd - 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 LIST_HEAD(create_callbacks);
31 static LIST_HEAD(destroy_callbacks);
33 static const struct blobmsg_policy new_policy = {
34 .name = "timeout", .type = BLOBMSG_TYPE_INT32
37 static const struct blobmsg_policy sid_policy = {
38 .name = "sid", .type = BLOBMSG_TYPE_STRING
46 static const struct blobmsg_policy set_policy[__RPC_SS_MAX] = {
47 [RPC_SS_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
48 [RPC_SS_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE },
56 static const struct blobmsg_policy get_policy[__RPC_SG_MAX] = {
57 [RPC_SG_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
58 [RPC_SG_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY },
67 static const struct blobmsg_policy acl_policy[__RPC_SA_MAX] = {
68 [RPC_SA_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
69 [RPC_SA_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
70 [RPC_SA_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY },
80 static const struct blobmsg_policy perm_policy[__RPC_SP_MAX] = {
81 [RPC_SP_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
82 [RPC_SP_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING },
83 [RPC_SP_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_STRING },
84 [RPC_SP_FUNCTION] = { .name = "function", .type = BLOBMSG_TYPE_STRING },
88 * Keys in the AVL tree contain all pattern characters up to the first wildcard.
89 * To look up entries, start with the last entry that has a key less than or
90 * equal to the method name, then work backwards as long as the AVL key still
91 * matches its counterpart in the object name
93 #define uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \
94 for (_acl = avl_find_le_element(_avl, _obj, _acl, avl); \
96 _acl = avl_is_first(_avl, &(_acl)->avl) ? NULL : \
97 avl_prev_element((_acl), avl))
99 #define uh_foreach_matching_acl(_acl, _avl, _obj, _func) \
100 uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \
101 if (!strncmp((_acl)->object, _obj, (_acl)->sort_len) && \
102 !fnmatch((_acl)->object, (_obj), FNM_NOESCAPE) && \
103 !fnmatch((_acl)->function, (_func), FNM_NOESCAPE))
106 rpc_random(char *dest)
108 unsigned char buf[16] = { 0 };
112 f = fopen("/dev/urandom", "r");
116 fread(buf, 1, sizeof(buf), f);
119 for (i = 0; i < sizeof(buf); i++)
120 sprintf(dest + (i<<1), "%02x", buf[i]);
124 rpc_session_dump_data(struct rpc_session *ses, struct blob_buf *b)
126 struct rpc_session_data *d;
128 avl_for_each_element(&ses->data, d, avl) {
129 blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr),
130 blobmsg_data(d->attr), blobmsg_data_len(d->attr));
135 rpc_session_dump_acls(struct rpc_session *ses, struct blob_buf *b)
137 struct rpc_session_acl *acl;
138 struct rpc_session_acl_scope *acl_scope;
139 const char *lastobj = NULL;
140 const char *lastscope = NULL;
141 void *c = NULL, *d = NULL;
143 avl_for_each_element(&ses->acls, acl_scope, avl) {
144 if (!lastscope || strcmp(acl_scope->avl.key, lastscope))
146 if (c) blobmsg_close_table(b, c);
147 c = blobmsg_open_table(b, acl_scope->avl.key);
153 avl_for_each_element(&acl_scope->acls, acl, avl) {
154 if (!lastobj || strcmp(acl->object, lastobj))
156 if (d) blobmsg_close_array(b, d);
157 d = blobmsg_open_array(b, acl->object);
160 blobmsg_add_string(b, NULL, acl->function);
161 lastobj = acl->object;
164 if (d) blobmsg_close_array(b, d);
167 if (c) blobmsg_close_table(b, c);
171 rpc_session_dump(struct rpc_session *ses,
172 struct ubus_context *ctx,
173 struct ubus_request_data *req)
177 blob_buf_init(&buf, 0);
179 blobmsg_add_string(&buf, "sid", ses->id);
180 blobmsg_add_u32(&buf, "timeout", ses->timeout);
181 blobmsg_add_u32(&buf, "expires", uloop_timeout_remaining(&ses->t) / 1000);
183 c = blobmsg_open_table(&buf, "acls");
184 rpc_session_dump_acls(ses, &buf);
185 blobmsg_close_table(&buf, c);
187 c = blobmsg_open_table(&buf, "data");
188 rpc_session_dump_data(ses, &buf);
189 blobmsg_close_table(&buf, c);
191 ubus_send_reply(ctx, req, buf.head);
195 rpc_touch_session(struct rpc_session *ses)
197 uloop_timeout_set(&ses->t, ses->timeout * 1000);
201 rpc_session_destroy(struct rpc_session *ses)
203 struct rpc_session_acl *acl, *nacl;
204 struct rpc_session_acl_scope *acl_scope, *nacl_scope;
205 struct rpc_session_data *data, *ndata;
206 struct rpc_session_cb *cb;
208 list_for_each_entry(cb, &destroy_callbacks, list)
209 cb->cb(ses, cb->priv);
211 uloop_timeout_cancel(&ses->t);
213 avl_for_each_element_safe(&ses->acls, acl_scope, avl, nacl_scope) {
214 avl_remove_all_elements(&acl_scope->acls, acl, avl, nacl)
217 avl_delete(&ses->acls, &acl_scope->avl);
221 avl_remove_all_elements(&ses->data, data, avl, ndata)
224 avl_delete(&sessions, &ses->avl);
228 static void rpc_session_timeout(struct uloop_timeout *t)
230 struct rpc_session *ses;
232 ses = container_of(t, struct rpc_session, t);
233 rpc_session_destroy(ses);
236 static struct rpc_session *
237 rpc_session_create(int timeout)
239 struct rpc_session *ses;
240 struct rpc_session_cb *cb;
242 ses = calloc(1, sizeof(*ses));
246 ses->timeout = timeout;
247 ses->avl.key = ses->id;
250 avl_insert(&sessions, &ses->avl);
251 avl_init(&ses->acls, avl_strcmp, true, NULL);
252 avl_init(&ses->data, avl_strcmp, false, NULL);
254 ses->t.cb = rpc_session_timeout;
255 rpc_touch_session(ses);
257 list_for_each_entry(cb, &create_callbacks, list)
258 cb->cb(ses, cb->priv);
263 static struct rpc_session *
264 rpc_session_get(const char *id)
266 struct rpc_session *ses;
268 ses = avl_find_element(&sessions, id, ses, avl);
272 rpc_touch_session(ses);
277 rpc_handle_create(struct ubus_context *ctx, struct ubus_object *obj,
278 struct ubus_request_data *req, const char *method,
279 struct blob_attr *msg)
281 struct rpc_session *ses;
282 struct blob_attr *tb;
283 int timeout = RPC_DEFAULT_SESSION_TIMEOUT;
285 blobmsg_parse(&new_policy, 1, &tb, blob_data(msg), blob_len(msg));
287 timeout = blobmsg_get_u32(tb);
289 ses = rpc_session_create(timeout);
291 rpc_session_dump(ses, ctx, req);
297 rpc_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
298 struct ubus_request_data *req, const char *method,
299 struct blob_attr *msg)
301 struct rpc_session *ses;
302 struct blob_attr *tb;
304 blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
307 avl_for_each_element(&sessions, ses, avl)
308 rpc_session_dump(ses, ctx, req);
312 ses = rpc_session_get(blobmsg_data(tb));
314 return UBUS_STATUS_NOT_FOUND;
316 rpc_session_dump(ses, ctx, req);
322 uh_id_len(const char *str)
324 return strcspn(str, "*?[");
328 rpc_session_grant(struct rpc_session *ses, struct ubus_context *ctx,
329 const char *scope, const char *object, const char *function)
331 struct rpc_session_acl *acl;
332 struct rpc_session_acl_scope *acl_scope;
333 char *new_scope, *new_obj, *new_func, *new_id;
336 if (!object || !function)
337 return UBUS_STATUS_INVALID_ARGUMENT;
339 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
342 uh_foreach_matching_acl_prefix(acl, &acl_scope->acls, object, function) {
343 if (!strcmp(acl->object, object) &&
344 !strcmp(acl->function, function))
350 acl_scope = calloc_a(sizeof(*acl_scope),
351 &new_scope, strlen(scope) + 1);
354 return UBUS_STATUS_UNKNOWN_ERROR;
356 acl_scope->avl.key = strcpy(new_scope, scope);
357 avl_init(&acl_scope->acls, avl_strcmp, true, NULL);
358 avl_insert(&ses->acls, &acl_scope->avl);
361 id_len = uh_id_len(object);
362 acl = calloc_a(sizeof(*acl),
363 &new_obj, strlen(object) + 1,
364 &new_func, strlen(function) + 1,
365 &new_id, id_len + 1);
368 return UBUS_STATUS_UNKNOWN_ERROR;
370 acl->object = strcpy(new_obj, object);
371 acl->function = strcpy(new_func, function);
372 acl->avl.key = strncpy(new_id, object, id_len);
373 avl_insert(&acl_scope->acls, &acl->avl);
379 rpc_session_revoke(struct rpc_session *ses, struct ubus_context *ctx,
380 const char *scope, const char *object, const char *function)
382 struct rpc_session_acl *acl, *next;
383 struct rpc_session_acl_scope *acl_scope;
387 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
392 if (!object && !function) {
393 avl_remove_all_elements(&acl_scope->acls, acl, avl, next)
395 avl_delete(&ses->acls, &acl_scope->avl);
400 id_len = uh_id_len(object);
401 id = alloca(id_len + 1);
402 strncpy(id, object, id_len);
405 acl = avl_find_element(&acl_scope->acls, id, acl, avl);
407 if (!avl_is_last(&acl_scope->acls, &acl->avl))
408 next = avl_next_element(acl, avl);
412 if (strcmp(id, acl->avl.key) != 0)
415 if (!strcmp(acl->object, object) &&
416 !strcmp(acl->function, function)) {
417 avl_delete(&acl_scope->acls, &acl->avl);
423 if (avl_is_empty(&acl_scope->acls)) {
424 avl_delete(&ses->acls, &acl_scope->avl);
433 rpc_handle_acl(struct ubus_context *ctx, struct ubus_object *obj,
434 struct ubus_request_data *req, const char *method,
435 struct blob_attr *msg)
437 struct rpc_session *ses;
438 struct blob_attr *tb[__RPC_SA_MAX];
439 struct blob_attr *attr, *sattr;
440 const char *object, *function;
441 const char *scope = "ubus";
444 int (*cb)(struct rpc_session *ses, struct ubus_context *ctx,
445 const char *scope, const char *object, const char *function);
447 blobmsg_parse(acl_policy, __RPC_SA_MAX, tb, blob_data(msg), blob_len(msg));
450 return UBUS_STATUS_INVALID_ARGUMENT;
452 ses = rpc_session_get(blobmsg_data(tb[RPC_SA_SID]));
454 return UBUS_STATUS_NOT_FOUND;
456 if (tb[RPC_SA_SCOPE])
457 scope = blobmsg_data(tb[RPC_SA_SCOPE]);
459 if (!strcmp(method, "grant"))
460 cb = rpc_session_grant;
462 cb = rpc_session_revoke;
464 if (!tb[RPC_SA_OBJECTS])
465 return cb(ses, ctx, scope, NULL, NULL);
467 blobmsg_for_each_attr(attr, tb[RPC_SA_OBJECTS], rem1) {
468 if (blob_id(attr) != BLOBMSG_TYPE_ARRAY)
474 blobmsg_for_each_attr(sattr, attr, rem2) {
475 if (blob_id(sattr) != BLOBMSG_TYPE_STRING)
479 object = blobmsg_data(sattr);
481 function = blobmsg_data(sattr);
486 if (object && function)
487 cb(ses, ctx, scope, object, function);
494 rpc_session_acl_allowed(struct rpc_session *ses, const char *scope,
495 const char *obj, const char *fun)
497 struct rpc_session_acl *acl;
498 struct rpc_session_acl_scope *acl_scope;
500 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
503 uh_foreach_matching_acl(acl, &acl_scope->acls, obj, fun)
511 rpc_handle_access(struct ubus_context *ctx, struct ubus_object *obj,
512 struct ubus_request_data *req, const char *method,
513 struct blob_attr *msg)
515 struct rpc_session *ses;
516 struct blob_attr *tb[__RPC_SP_MAX];
517 const char *scope = "ubus";
520 blobmsg_parse(perm_policy, __RPC_SP_MAX, tb, blob_data(msg), blob_len(msg));
522 if (!tb[RPC_SP_SID] || !tb[RPC_SP_OBJECT] || !tb[RPC_SP_FUNCTION])
523 return UBUS_STATUS_INVALID_ARGUMENT;
525 ses = rpc_session_get(blobmsg_data(tb[RPC_SP_SID]));
527 return UBUS_STATUS_NOT_FOUND;
529 if (tb[RPC_SP_SCOPE])
530 scope = blobmsg_data(tb[RPC_SP_SCOPE]);
532 allow = rpc_session_acl_allowed(ses, scope,
533 blobmsg_data(tb[RPC_SP_OBJECT]),
534 blobmsg_data(tb[RPC_SP_FUNCTION]));
536 blob_buf_init(&buf, 0);
537 blobmsg_add_u8(&buf, "access", allow);
538 ubus_send_reply(ctx, req, buf.head);
544 rpc_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
545 struct ubus_request_data *req, const char *method,
546 struct blob_attr *msg)
548 struct rpc_session *ses;
549 struct rpc_session_data *data;
550 struct blob_attr *tb[__RPC_SA_MAX];
551 struct blob_attr *attr;
554 blobmsg_parse(set_policy, __RPC_SS_MAX, tb, blob_data(msg), blob_len(msg));
556 if (!tb[RPC_SS_SID] || !tb[RPC_SS_VALUES])
557 return UBUS_STATUS_INVALID_ARGUMENT;
559 ses = rpc_session_get(blobmsg_data(tb[RPC_SS_SID]));
561 return UBUS_STATUS_NOT_FOUND;
563 blobmsg_for_each_attr(attr, tb[RPC_SS_VALUES], rem) {
564 if (!blobmsg_name(attr)[0])
567 data = avl_find_element(&ses->data, blobmsg_name(attr), data, avl);
569 avl_delete(&ses->data, &data->avl);
573 data = calloc(1, sizeof(*data) + blob_pad_len(attr));
577 memcpy(data->attr, attr, blob_pad_len(attr));
578 data->avl.key = blobmsg_name(data->attr);
579 avl_insert(&ses->data, &data->avl);
586 rpc_handle_get(struct ubus_context *ctx, struct ubus_object *obj,
587 struct ubus_request_data *req, const char *method,
588 struct blob_attr *msg)
590 struct rpc_session *ses;
591 struct rpc_session_data *data;
592 struct blob_attr *tb[__RPC_SA_MAX];
593 struct blob_attr *attr;
597 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
600 return UBUS_STATUS_INVALID_ARGUMENT;
602 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
604 return UBUS_STATUS_NOT_FOUND;
606 blob_buf_init(&buf, 0);
607 c = blobmsg_open_table(&buf, "values");
610 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
611 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
614 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
618 blobmsg_add_field(&buf, blobmsg_type(data->attr),
619 blobmsg_name(data->attr),
620 blobmsg_data(data->attr),
621 blobmsg_data_len(data->attr));
624 rpc_session_dump_data(ses, &buf);
626 blobmsg_close_table(&buf, c);
627 ubus_send_reply(ctx, req, buf.head);
633 rpc_handle_unset(struct ubus_context *ctx, struct ubus_object *obj,
634 struct ubus_request_data *req, const char *method,
635 struct blob_attr *msg)
637 struct rpc_session *ses;
638 struct rpc_session_data *data, *ndata;
639 struct blob_attr *tb[__RPC_SA_MAX];
640 struct blob_attr *attr;
643 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
646 return UBUS_STATUS_INVALID_ARGUMENT;
648 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
650 return UBUS_STATUS_NOT_FOUND;
652 if (!tb[RPC_SG_KEYS]) {
653 avl_remove_all_elements(&ses->data, data, avl, ndata)
658 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
659 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
662 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
666 avl_delete(&ses->data, &data->avl);
674 rpc_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj,
675 struct ubus_request_data *req, const char *method,
676 struct blob_attr *msg)
678 struct rpc_session *ses;
679 struct blob_attr *tb;
681 blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
684 return UBUS_STATUS_INVALID_ARGUMENT;
686 ses = rpc_session_get(blobmsg_data(tb));
688 return UBUS_STATUS_NOT_FOUND;
690 rpc_session_destroy(ses);
695 int rpc_session_api_init(struct ubus_context *ctx)
697 static const struct ubus_method session_methods[] = {
698 UBUS_METHOD("create", rpc_handle_create, &new_policy),
699 UBUS_METHOD("list", rpc_handle_list, &sid_policy),
700 UBUS_METHOD("grant", rpc_handle_acl, acl_policy),
701 UBUS_METHOD("revoke", rpc_handle_acl, acl_policy),
702 UBUS_METHOD("access", rpc_handle_access, perm_policy),
703 UBUS_METHOD("set", rpc_handle_set, set_policy),
704 UBUS_METHOD("get", rpc_handle_get, get_policy),
705 UBUS_METHOD("unset", rpc_handle_unset, get_policy),
706 UBUS_METHOD("destroy", rpc_handle_destroy, &sid_policy),
709 static struct ubus_object_type session_type =
710 UBUS_OBJECT_TYPE("luci-rpc-session", session_methods);
712 static struct ubus_object obj = {
714 .type = &session_type,
715 .methods = session_methods,
716 .n_methods = ARRAY_SIZE(session_methods),
719 avl_init(&sessions, avl_strcmp, false, NULL);
721 return ubus_add_object(ctx, &obj);
724 bool rpc_session_access(const char *sid, const char *scope,
725 const char *object, const char *function)
727 struct rpc_session *ses = rpc_session_get(sid);
732 return rpc_session_acl_allowed(ses, scope, object, function);
735 void rpc_session_create_cb(struct rpc_session_cb *cb)
738 list_add(&cb->list, &create_callbacks);
741 void rpc_session_destroy_cb(struct rpc_session_cb *cb)
744 list_add(&cb->list, &destroy_callbacks);