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
);