main: only purge uci savedirs if not reloading
[project/rpcd.git] / session.c
1 /*
2  * rpcd - UBUS RPC server
3  *
4  *   Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
5  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
6  *
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.
10  *
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.
18  */
19
20 #include <libubox/avl-cmp.h>
21 #include <libubox/utils.h>
22 #include <libubus.h>
23 #include <fnmatch.h>
24
25 #include <rpcd/session.h>
26
27 static struct avl_tree sessions;
28 static struct blob_buf buf;
29
30 static LIST_HEAD(create_callbacks);
31 static LIST_HEAD(destroy_callbacks);
32
33 static const struct blobmsg_policy new_policy = {
34         .name = "timeout", .type = BLOBMSG_TYPE_INT32
35 };
36
37 static const struct blobmsg_policy sid_policy = {
38         .name = "sid", .type = BLOBMSG_TYPE_STRING
39 };
40
41 enum {
42         RPC_SS_SID,
43         RPC_SS_VALUES,
44         __RPC_SS_MAX,
45 };
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 },
49 };
50
51 enum {
52         RPC_SG_SID,
53         RPC_SG_KEYS,
54         __RPC_SG_MAX,
55 };
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 },
59 };
60
61 enum {
62         RPC_SA_SID,
63         RPC_SA_SCOPE,
64         RPC_SA_OBJECTS,
65         __RPC_SA_MAX,
66 };
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 },
71 };
72
73 enum {
74         RPC_SP_SID,
75         RPC_SP_SCOPE,
76         RPC_SP_OBJECT,
77         RPC_SP_FUNCTION,
78         __RPC_SP_MAX,
79 };
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 },
85 };
86
87 enum {
88         RPC_DUMP_SID,
89         RPC_DUMP_TIMEOUT,
90         RPC_DUMP_EXPIRES,
91         RPC_DUMP_ACLS,
92         RPC_DUMP_DATA,
93         __RPC_DUMP_MAX,
94 };
95 static const struct blobmsg_policy dump_policy[__RPC_DUMP_MAX] = {
96         [RPC_DUMP_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
97         [RPC_DUMP_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 },
98         [RPC_DUMP_EXPIRES] = { .name = "expires", .type = BLOBMSG_TYPE_INT32 },
99         [RPC_DUMP_ACLS] = { .name = "acls", .type = BLOBMSG_TYPE_TABLE },
100         [RPC_DUMP_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE },
101 };
102
103 /*
104  * Keys in the AVL tree contain all pattern characters up to the first wildcard.
105  * To look up entries, start with the last entry that has a key less than or
106  * equal to the method name, then work backwards as long as the AVL key still
107  * matches its counterpart in the object name
108  */
109 #define uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func)         \
110         for (_acl = avl_find_le_element(_avl, _obj, _acl, avl);                 \
111              _acl;                                                                                                              \
112              _acl = avl_is_first(_avl, &(_acl)->avl) ? NULL :                   \
113                     avl_prev_element((_acl), avl))
114
115 #define uh_foreach_matching_acl(_acl, _avl, _obj, _func)                        \
116         uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func)                 \
117                 if (!strncmp((_acl)->object, _obj, (_acl)->sort_len) &&         \
118                     !fnmatch((_acl)->object, (_obj), FNM_NOESCAPE) &&           \
119                     !fnmatch((_acl)->function, (_func), FNM_NOESCAPE))
120
121 static void
122 rpc_random(char *dest)
123 {
124         unsigned char buf[16] = { 0 };
125         FILE *f;
126         int i;
127
128         f = fopen("/dev/urandom", "r");
129         if (!f)
130                 return;
131
132         fread(buf, 1, sizeof(buf), f);
133         fclose(f);
134
135         for (i = 0; i < sizeof(buf); i++)
136                 sprintf(dest + (i<<1), "%02x", buf[i]);
137 }
138
139 static void
140 rpc_session_dump_data(struct rpc_session *ses, struct blob_buf *b)
141 {
142         struct rpc_session_data *d;
143
144         avl_for_each_element(&ses->data, d, avl) {
145                 blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr),
146                                   blobmsg_data(d->attr), blobmsg_data_len(d->attr));
147         }
148 }
149
150 static void
151 rpc_session_dump_acls(struct rpc_session *ses, struct blob_buf *b)
152 {
153         struct rpc_session_acl *acl;
154         struct rpc_session_acl_scope *acl_scope;
155         const char *lastobj = NULL;
156         const char *lastscope = NULL;
157         void *c = NULL, *d = NULL;
158
159         avl_for_each_element(&ses->acls, acl_scope, avl) {
160                 if (!lastscope || strcmp(acl_scope->avl.key, lastscope))
161                 {
162                         if (c) blobmsg_close_table(b, c);
163                         c = blobmsg_open_table(b, acl_scope->avl.key);
164                         lastobj = NULL;
165                 }
166
167                 d = NULL;
168
169                 avl_for_each_element(&acl_scope->acls, acl, avl) {
170                         if (!lastobj || strcmp(acl->object, lastobj))
171                         {
172                                 if (d) blobmsg_close_array(b, d);
173                                 d = blobmsg_open_array(b, acl->object);
174                         }
175
176                         blobmsg_add_string(b, NULL, acl->function);
177                         lastobj = acl->object;
178                 }
179
180                 if (d) blobmsg_close_array(b, d);
181         }
182
183         if (c) blobmsg_close_table(b, c);
184 }
185
186 static void
187 rpc_session_to_blob(struct rpc_session *ses)
188 {
189         void *c;
190
191         blob_buf_init(&buf, 0);
192
193         blobmsg_add_string(&buf, "sid", ses->id);
194         blobmsg_add_u32(&buf, "timeout", ses->timeout);
195         blobmsg_add_u32(&buf, "expires", uloop_timeout_remaining(&ses->t) / 1000);
196
197         c = blobmsg_open_table(&buf, "acls");
198         rpc_session_dump_acls(ses, &buf);
199         blobmsg_close_table(&buf, c);
200
201         c = blobmsg_open_table(&buf, "data");
202         rpc_session_dump_data(ses, &buf);
203         blobmsg_close_table(&buf, c);
204 }
205
206 static void
207 rpc_session_dump(struct rpc_session *ses, struct ubus_context *ctx,
208                  struct ubus_request_data *req)
209 {
210         rpc_session_to_blob(ses);
211
212         ubus_send_reply(ctx, req, buf.head);
213 }
214
215 static void
216 rpc_touch_session(struct rpc_session *ses)
217 {
218         uloop_timeout_set(&ses->t, ses->timeout * 1000);
219 }
220
221 static void
222 rpc_session_destroy(struct rpc_session *ses)
223 {
224         struct rpc_session_acl *acl, *nacl;
225         struct rpc_session_acl_scope *acl_scope, *nacl_scope;
226         struct rpc_session_data *data, *ndata;
227         struct rpc_session_cb *cb;
228
229         list_for_each_entry(cb, &destroy_callbacks, list)
230                 cb->cb(ses, cb->priv);
231
232         uloop_timeout_cancel(&ses->t);
233
234         avl_for_each_element_safe(&ses->acls, acl_scope, avl, nacl_scope) {
235                 avl_remove_all_elements(&acl_scope->acls, acl, avl, nacl)
236                         free(acl);
237
238                 avl_delete(&ses->acls, &acl_scope->avl);
239                 free(acl_scope);
240         }
241
242         avl_remove_all_elements(&ses->data, data, avl, ndata)
243                 free(data);
244
245         avl_delete(&sessions, &ses->avl);
246         free(ses);
247 }
248
249 static void rpc_session_timeout(struct uloop_timeout *t)
250 {
251         struct rpc_session *ses;
252
253         ses = container_of(t, struct rpc_session, t);
254         rpc_session_destroy(ses);
255 }
256
257 static struct rpc_session *
258 rpc_session_new(void)
259 {
260         struct rpc_session *ses;
261
262         ses = calloc(1, sizeof(*ses));
263
264         if (!ses)
265                 return NULL;
266
267         ses->avl.key = ses->id;
268
269         avl_init(&ses->acls, avl_strcmp, true, NULL);
270         avl_init(&ses->data, avl_strcmp, false, NULL);
271
272         ses->t.cb = rpc_session_timeout;
273
274         return ses;
275 }
276
277 static struct rpc_session *
278 rpc_session_create(int timeout)
279 {
280         struct rpc_session *ses;
281         struct rpc_session_cb *cb;
282
283         ses = rpc_session_new();
284
285         if (!ses)
286                 return NULL;
287
288         rpc_random(ses->id);
289
290         ses->timeout = timeout;
291
292         avl_insert(&sessions, &ses->avl);
293
294         rpc_touch_session(ses);
295
296         list_for_each_entry(cb, &create_callbacks, list)
297                 cb->cb(ses, cb->priv);
298
299         return ses;
300 }
301
302 static struct rpc_session *
303 rpc_session_get(const char *id)
304 {
305         struct rpc_session *ses;
306
307         ses = avl_find_element(&sessions, id, ses, avl);
308         if (!ses)
309                 return NULL;
310
311         rpc_touch_session(ses);
312         return ses;
313 }
314
315 static int
316 rpc_handle_create(struct ubus_context *ctx, struct ubus_object *obj,
317                   struct ubus_request_data *req, const char *method,
318                   struct blob_attr *msg)
319 {
320         struct rpc_session *ses;
321         struct blob_attr *tb;
322         int timeout = RPC_DEFAULT_SESSION_TIMEOUT;
323
324         blobmsg_parse(&new_policy, 1, &tb, blob_data(msg), blob_len(msg));
325         if (tb)
326                 timeout = blobmsg_get_u32(tb);
327
328         ses = rpc_session_create(timeout);
329         if (ses)
330                 rpc_session_dump(ses, ctx, req);
331
332         return 0;
333 }
334
335 static int
336 rpc_handle_list(struct ubus_context *ctx, struct ubus_object *obj,
337                 struct ubus_request_data *req, const char *method,
338                 struct blob_attr *msg)
339 {
340         struct rpc_session *ses;
341         struct blob_attr *tb;
342
343         blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
344
345         if (!tb) {
346                 avl_for_each_element(&sessions, ses, avl)
347                         rpc_session_dump(ses, ctx, req);
348                 return 0;
349         }
350
351         ses = rpc_session_get(blobmsg_data(tb));
352         if (!ses)
353                 return UBUS_STATUS_NOT_FOUND;
354
355         rpc_session_dump(ses, ctx, req);
356
357         return 0;
358 }
359
360 static int
361 uh_id_len(const char *str)
362 {
363         return strcspn(str, "*?[");
364 }
365
366 static int
367 rpc_session_grant(struct rpc_session *ses, struct ubus_context *ctx,
368                   const char *scope, const char *object, const char *function)
369 {
370         struct rpc_session_acl *acl;
371         struct rpc_session_acl_scope *acl_scope;
372         char *new_scope, *new_obj, *new_func, *new_id;
373         int id_len;
374
375         if (!object || !function)
376                 return UBUS_STATUS_INVALID_ARGUMENT;
377
378         acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
379
380         if (acl_scope) {
381                 uh_foreach_matching_acl_prefix(acl, &acl_scope->acls, object, function) {
382                         if (!strcmp(acl->object, object) &&
383                                 !strcmp(acl->function, function))
384                                 return 0;
385                 }
386         }
387
388         if (!acl_scope) {
389                 acl_scope = calloc_a(sizeof(*acl_scope),
390                                      &new_scope, strlen(scope) + 1);
391
392                 if (!acl_scope)
393                         return UBUS_STATUS_UNKNOWN_ERROR;
394
395                 acl_scope->avl.key = strcpy(new_scope, scope);
396                 avl_init(&acl_scope->acls, avl_strcmp, true, NULL);
397                 avl_insert(&ses->acls, &acl_scope->avl);
398         }
399
400         id_len = uh_id_len(object);
401         acl = calloc_a(sizeof(*acl),
402                 &new_obj, strlen(object) + 1,
403                 &new_func, strlen(function) + 1,
404                 &new_id, id_len + 1);
405
406         if (!acl)
407                 return UBUS_STATUS_UNKNOWN_ERROR;
408
409         acl->object = strcpy(new_obj, object);
410         acl->function = strcpy(new_func, function);
411         acl->avl.key = strncpy(new_id, object, id_len);
412         avl_insert(&acl_scope->acls, &acl->avl);
413
414         return 0;
415 }
416
417 static int
418 rpc_session_revoke(struct rpc_session *ses, struct ubus_context *ctx,
419                    const char *scope, const char *object, const char *function)
420 {
421         struct rpc_session_acl *acl, *next;
422         struct rpc_session_acl_scope *acl_scope;
423         int id_len;
424         char *id;
425
426         acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
427
428         if (!acl_scope)
429                 return 0;
430
431         if (!object && !function) {
432                 avl_remove_all_elements(&acl_scope->acls, acl, avl, next)
433                         free(acl);
434                 avl_delete(&ses->acls, &acl_scope->avl);
435                 free(acl_scope);
436                 return 0;
437         }
438
439         id_len = uh_id_len(object);
440         id = alloca(id_len + 1);
441         strncpy(id, object, id_len);
442         id[id_len] = 0;
443
444         acl = avl_find_element(&acl_scope->acls, id, acl, avl);
445         while (acl) {
446                 if (!avl_is_last(&acl_scope->acls, &acl->avl))
447                         next = avl_next_element(acl, avl);
448                 else
449                         next = NULL;
450
451                 if (strcmp(id, acl->avl.key) != 0)
452                         break;
453
454                 if (!strcmp(acl->object, object) &&
455                     !strcmp(acl->function, function)) {
456                         avl_delete(&acl_scope->acls, &acl->avl);
457                         free(acl);
458                 }
459                 acl = next;
460         }
461
462         if (avl_is_empty(&acl_scope->acls)) {
463                 avl_delete(&ses->acls, &acl_scope->avl);
464                 free(acl_scope);
465         }
466
467         return 0;
468 }
469
470
471 static int
472 rpc_handle_acl(struct ubus_context *ctx, struct ubus_object *obj,
473                struct ubus_request_data *req, const char *method,
474                struct blob_attr *msg)
475 {
476         struct rpc_session *ses;
477         struct blob_attr *tb[__RPC_SA_MAX];
478         struct blob_attr *attr, *sattr;
479         const char *object, *function;
480         const char *scope = "ubus";
481         int rem1, rem2;
482
483         int (*cb)(struct rpc_session *ses, struct ubus_context *ctx,
484                   const char *scope, const char *object, const char *function);
485
486         blobmsg_parse(acl_policy, __RPC_SA_MAX, tb, blob_data(msg), blob_len(msg));
487
488         if (!tb[RPC_SA_SID])
489                 return UBUS_STATUS_INVALID_ARGUMENT;
490
491         ses = rpc_session_get(blobmsg_data(tb[RPC_SA_SID]));
492         if (!ses)
493                 return UBUS_STATUS_NOT_FOUND;
494
495         if (tb[RPC_SA_SCOPE])
496                 scope = blobmsg_data(tb[RPC_SA_SCOPE]);
497
498         if (!strcmp(method, "grant"))
499                 cb = rpc_session_grant;
500         else
501                 cb = rpc_session_revoke;
502
503         if (!tb[RPC_SA_OBJECTS])
504                 return cb(ses, ctx, scope, NULL, NULL);
505
506         blobmsg_for_each_attr(attr, tb[RPC_SA_OBJECTS], rem1) {
507                 if (blob_id(attr) != BLOBMSG_TYPE_ARRAY)
508                         continue;
509
510                 object = NULL;
511                 function = NULL;
512
513                 blobmsg_for_each_attr(sattr, attr, rem2) {
514                         if (blob_id(sattr) != BLOBMSG_TYPE_STRING)
515                                 continue;
516
517                         if (!object)
518                                 object = blobmsg_data(sattr);
519                         else if (!function)
520                                 function = blobmsg_data(sattr);
521                         else
522                                 break;
523                 }
524
525                 if (object && function)
526                         cb(ses, ctx, scope, object, function);
527         }
528
529         return 0;
530 }
531
532 static bool
533 rpc_session_acl_allowed(struct rpc_session *ses, const char *scope,
534                         const char *obj, const char *fun)
535 {
536         struct rpc_session_acl *acl;
537         struct rpc_session_acl_scope *acl_scope;
538
539         acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl);
540
541         if (acl_scope) {
542                 uh_foreach_matching_acl(acl, &acl_scope->acls, obj, fun)
543                         return true;
544         }
545
546         return false;
547 }
548
549 static int
550 rpc_handle_access(struct ubus_context *ctx, struct ubus_object *obj,
551                   struct ubus_request_data *req, const char *method,
552                   struct blob_attr *msg)
553 {
554         struct rpc_session *ses;
555         struct blob_attr *tb[__RPC_SP_MAX];
556         const char *scope = "ubus";
557         bool allow;
558
559         blobmsg_parse(perm_policy, __RPC_SP_MAX, tb, blob_data(msg), blob_len(msg));
560
561         if (!tb[RPC_SP_SID] || !tb[RPC_SP_OBJECT] || !tb[RPC_SP_FUNCTION])
562                 return UBUS_STATUS_INVALID_ARGUMENT;
563
564         ses = rpc_session_get(blobmsg_data(tb[RPC_SP_SID]));
565         if (!ses)
566                 return UBUS_STATUS_NOT_FOUND;
567
568         if (tb[RPC_SP_SCOPE])
569                 scope = blobmsg_data(tb[RPC_SP_SCOPE]);
570
571         allow = rpc_session_acl_allowed(ses, scope,
572                                                                         blobmsg_data(tb[RPC_SP_OBJECT]),
573                                                                         blobmsg_data(tb[RPC_SP_FUNCTION]));
574
575         blob_buf_init(&buf, 0);
576         blobmsg_add_u8(&buf, "access", allow);
577         ubus_send_reply(ctx, req, buf.head);
578
579         return 0;
580 }
581
582 static void
583 rpc_session_set(struct rpc_session *ses, const char *key, struct blob_attr *val)
584 {
585         struct rpc_session_data *data;
586
587         data = avl_find_element(&ses->data, key, data, avl);
588         if (data) {
589                 avl_delete(&ses->data, &data->avl);
590                 free(data);
591         }
592
593         data = calloc(1, sizeof(*data) + blob_pad_len(val));
594         if (!data)
595                 return;
596
597         memcpy(data->attr, val, blob_pad_len(val));
598         data->avl.key = blobmsg_name(data->attr);
599         avl_insert(&ses->data, &data->avl);
600 }
601
602 static int
603 rpc_handle_set(struct ubus_context *ctx, struct ubus_object *obj,
604                struct ubus_request_data *req, const char *method,
605                struct blob_attr *msg)
606 {
607         struct rpc_session *ses;
608         struct blob_attr *tb[__RPC_SA_MAX];
609         struct blob_attr *attr;
610         int rem;
611
612         blobmsg_parse(set_policy, __RPC_SS_MAX, tb, blob_data(msg), blob_len(msg));
613
614         if (!tb[RPC_SS_SID] || !tb[RPC_SS_VALUES])
615                 return UBUS_STATUS_INVALID_ARGUMENT;
616
617         ses = rpc_session_get(blobmsg_data(tb[RPC_SS_SID]));
618         if (!ses)
619                 return UBUS_STATUS_NOT_FOUND;
620
621         blobmsg_for_each_attr(attr, tb[RPC_SS_VALUES], rem) {
622                 if (!blobmsg_name(attr)[0])
623                         continue;
624
625                 rpc_session_set(ses, blobmsg_name(attr), attr);
626         }
627
628         return 0;
629 }
630
631 static int
632 rpc_handle_get(struct ubus_context *ctx, struct ubus_object *obj,
633                struct ubus_request_data *req, const char *method,
634                struct blob_attr *msg)
635 {
636         struct rpc_session *ses;
637         struct rpc_session_data *data;
638         struct blob_attr *tb[__RPC_SA_MAX];
639         struct blob_attr *attr;
640         void *c;
641         int rem;
642
643         blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
644
645         if (!tb[RPC_SG_SID])
646                 return UBUS_STATUS_INVALID_ARGUMENT;
647
648         ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
649         if (!ses)
650                 return UBUS_STATUS_NOT_FOUND;
651
652         blob_buf_init(&buf, 0);
653         c = blobmsg_open_table(&buf, "values");
654
655         if (tb[RPC_SG_KEYS])
656                 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
657                         if (blob_id(attr) != BLOBMSG_TYPE_STRING)
658                                 continue;
659
660                         data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
661                         if (!data)
662                                 continue;
663
664                         blobmsg_add_field(&buf, blobmsg_type(data->attr),
665                                           blobmsg_name(data->attr),
666                                           blobmsg_data(data->attr),
667                                           blobmsg_data_len(data->attr));
668                 }
669         else
670                 rpc_session_dump_data(ses, &buf);
671
672         blobmsg_close_table(&buf, c);
673         ubus_send_reply(ctx, req, buf.head);
674
675         return 0;
676 }
677
678 static int
679 rpc_handle_unset(struct ubus_context *ctx, struct ubus_object *obj,
680                  struct ubus_request_data *req, const char *method,
681                  struct blob_attr *msg)
682 {
683         struct rpc_session *ses;
684         struct rpc_session_data *data, *ndata;
685         struct blob_attr *tb[__RPC_SA_MAX];
686         struct blob_attr *attr;
687         int rem;
688
689         blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg));
690
691         if (!tb[RPC_SG_SID])
692                 return UBUS_STATUS_INVALID_ARGUMENT;
693
694         ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID]));
695         if (!ses)
696                 return UBUS_STATUS_NOT_FOUND;
697
698         if (!tb[RPC_SG_KEYS]) {
699                 avl_remove_all_elements(&ses->data, data, avl, ndata)
700                         free(data);
701                 return 0;
702         }
703
704         blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) {
705                 if (blob_id(attr) != BLOBMSG_TYPE_STRING)
706                         continue;
707
708                 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl);
709                 if (!data)
710                         continue;
711
712                 avl_delete(&ses->data, &data->avl);
713                 free(data);
714         }
715
716         return 0;
717 }
718
719 static int
720 rpc_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj,
721                    struct ubus_request_data *req, const char *method,
722                    struct blob_attr *msg)
723 {
724         struct rpc_session *ses;
725         struct blob_attr *tb;
726
727         blobmsg_parse(&sid_policy, 1, &tb, blob_data(msg), blob_len(msg));
728
729         if (!tb)
730                 return UBUS_STATUS_INVALID_ARGUMENT;
731
732         ses = rpc_session_get(blobmsg_data(tb));
733         if (!ses)
734                 return UBUS_STATUS_NOT_FOUND;
735
736         rpc_session_destroy(ses);
737
738         return 0;
739 }
740
741
742 static bool
743 rpc_validate_sid(const char *id)
744 {
745         if (!id)
746                 return false;
747
748         if (strlen(id) != RPC_SID_LEN)
749                 return false;
750
751         while (*id)
752                 if (!isxdigit(*id++))
753                         return false;
754
755         return true;
756 }
757
758 static int
759 rpc_blob_to_file(const char *path, struct blob_attr *attr)
760 {
761         int fd, len;
762
763         fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
764
765         if (fd < 0)
766                 return fd;
767
768         len = write(fd, attr, blob_pad_len(attr));
769
770         close(fd);
771
772         if (len != blob_pad_len(attr))
773         {
774                 unlink(path);
775                 return -1;
776         }
777
778         return len;
779 }
780
781 static struct blob_attr *
782 rpc_blob_from_file(const char *path)
783 {
784         int fd = -1, len;
785         struct stat s;
786         struct blob_attr head, *attr = NULL;
787
788         if (stat(path, &s) || !S_ISREG(s.st_mode))
789                 return NULL;
790
791         fd = open(path, O_RDONLY);
792
793         if (fd < 0)
794                 goto fail;
795
796         len = read(fd, &head, sizeof(head));
797
798         if (len != sizeof(head) || blob_pad_len(&head) != s.st_size)
799                 goto fail;
800
801         attr = calloc(1, s.st_size);
802
803         if (!attr)
804                 goto fail;
805
806         memcpy(attr, &head, sizeof(head));
807
808         len += read(fd, (char *)attr + sizeof(head), s.st_size - sizeof(head));
809
810         if (len != blob_pad_len(&head))
811                 goto fail;
812
813         return attr;
814
815 fail:
816         if (fd >= 0)
817                 close(fd);
818
819         if (attr)
820                 free(attr);
821
822         return NULL;
823 }
824
825 static bool
826 rpc_session_from_blob(struct blob_attr *attr)
827 {
828         int i, rem, rem2, rem3;
829         struct rpc_session *ses;
830         struct blob_attr *tb[__RPC_DUMP_MAX], *scope, *object, *function;
831
832         blobmsg_parse(dump_policy, __RPC_DUMP_MAX, tb,
833                       blob_data(attr), blob_len(attr));
834
835         for (i = 0; i < __RPC_DUMP_MAX; i++)
836                 if (!tb[i])
837                         return false;
838
839         ses = rpc_session_new();
840
841         if (!ses)
842                 return false;
843
844         memcpy(ses->id, blobmsg_data(tb[RPC_DUMP_SID]), RPC_SID_LEN);
845
846         ses->timeout = blobmsg_get_u32(tb[RPC_DUMP_TIMEOUT]);
847
848         blobmsg_for_each_attr(scope, tb[RPC_DUMP_ACLS], rem) {
849                 blobmsg_for_each_attr(object, scope, rem2) {
850                         blobmsg_for_each_attr(function, object, rem3) {
851                                 rpc_session_grant(ses, NULL, blobmsg_name(scope),
852                                                              blobmsg_name(object),
853                                                              blobmsg_data(function));
854                         }
855                 }
856         }
857
858         blobmsg_for_each_attr(object, tb[RPC_DUMP_DATA], rem) {
859                 rpc_session_set(ses, blobmsg_name(object), object);
860         }
861
862         avl_insert(&sessions, &ses->avl);
863
864         uloop_timeout_set(&ses->t, blobmsg_get_u32(tb[RPC_DUMP_EXPIRES]) * 1000);
865
866         return true;
867 }
868
869 int rpc_session_api_init(struct ubus_context *ctx)
870 {
871         static const struct ubus_method session_methods[] = {
872                 UBUS_METHOD("create",  rpc_handle_create,  &new_policy),
873                 UBUS_METHOD("list",    rpc_handle_list,    &sid_policy),
874                 UBUS_METHOD("grant",   rpc_handle_acl,     acl_policy),
875                 UBUS_METHOD("revoke",  rpc_handle_acl,     acl_policy),
876                 UBUS_METHOD("access",  rpc_handle_access,  perm_policy),
877                 UBUS_METHOD("set",     rpc_handle_set,     set_policy),
878                 UBUS_METHOD("get",     rpc_handle_get,     get_policy),
879                 UBUS_METHOD("unset",   rpc_handle_unset,   get_policy),
880                 UBUS_METHOD("destroy", rpc_handle_destroy, &sid_policy),
881         };
882
883         static struct ubus_object_type session_type =
884                 UBUS_OBJECT_TYPE("luci-rpc-session", session_methods);
885
886         static struct ubus_object obj = {
887                 .name = "session",
888                 .type = &session_type,
889                 .methods = session_methods,
890                 .n_methods = ARRAY_SIZE(session_methods),
891         };
892
893         avl_init(&sessions, avl_strcmp, false, NULL);
894
895         return ubus_add_object(ctx, &obj);
896 }
897
898 bool rpc_session_access(const char *sid, const char *scope,
899                         const char *object, const char *function)
900 {
901         struct rpc_session *ses = rpc_session_get(sid);
902
903         if (!ses)
904                 return false;
905
906         return rpc_session_acl_allowed(ses, scope, object, function);
907 }
908
909 void rpc_session_create_cb(struct rpc_session_cb *cb)
910 {
911         if (cb && cb->cb)
912                 list_add(&cb->list, &create_callbacks);
913 }
914
915 void rpc_session_destroy_cb(struct rpc_session_cb *cb)
916 {
917         if (cb && cb->cb)
918                 list_add(&cb->list, &destroy_callbacks);
919 }
920
921 void rpc_session_freeze(void)
922 {
923         struct stat s;
924         struct rpc_session *ses;
925         char path[PATH_MAX];
926
927         if (stat(RPC_SESSION_DIRECTORY, &s))
928                 mkdir(RPC_SESSION_DIRECTORY, 0700);
929
930         avl_for_each_element(&sessions, ses, avl) {
931                 snprintf(path, sizeof(path) - 1, RPC_SESSION_DIRECTORY "/%s", ses->id);
932                 rpc_session_to_blob(ses);
933                 rpc_blob_to_file(path, buf.head);
934         }
935 }
936
937 void rpc_session_thaw(void)
938 {
939         DIR *d;
940         char path[PATH_MAX];
941         struct dirent *e;
942         struct blob_attr *attr;
943
944         d = opendir(RPC_SESSION_DIRECTORY);
945
946         if (!d)
947                 return;
948
949         while ((e = readdir(d)) != NULL) {
950                 if (!rpc_validate_sid(e->d_name))
951                         continue;
952
953                 snprintf(path, sizeof(path) - 1,
954                          RPC_SESSION_DIRECTORY "/%s", e->d_name);
955
956                 attr = rpc_blob_from_file(path);
957
958                 if (attr) {
959                         rpc_session_from_blob(attr);
960                         free(attr);
961                 }
962
963                 unlink(path);
964         }
965
966         closedir(d);
967 }