rpcd: use "access-group" meta scope when checking menu permissions
[project/luci2/ui.git] / luci2 / src / io / login.c
1 /*
2  * luci-io - LuCI non-RPC helper
3  *
4  *   Copyright (C) 2013 Jo-Philipp Wich <jow@openwrt.org>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18
19 #include "login.h"
20
21
22 enum {
23         SES_NEW_SID,
24         __SES_NEW_MAX,
25 };
26
27 static const struct blobmsg_policy ses_new_policy[__SES_NEW_MAX] = {
28         [SES_NEW_SID] = { .name = "sid", .type = BLOBMSG_TYPE_STRING },
29 };
30
31 static bool
32 parse_acl_scope(struct blob_attr *acl_perm, struct blob_attr *acl_scope,
33                                 struct blob_buf *req)
34 {
35         struct blob_attr *acl_obj, *acl_func;
36         int rem, rem2;
37         void *c, *d;
38
39         if (!strcmp(blobmsg_name(acl_scope), "ubus") &&
40             blob_id(acl_scope) == BLOBMSG_TYPE_TABLE)
41         {
42                 blobmsg_add_string(req, "scope", blobmsg_name(acl_scope));
43                 c = blobmsg_open_array(req, "objects");
44
45                 blobmsg_for_each_attr(acl_obj, acl_scope, rem)
46                 {
47                         if (blob_id(acl_obj) != BLOBMSG_TYPE_ARRAY)
48                                 continue;
49
50                         blobmsg_for_each_attr(acl_func, acl_obj, rem2)
51                         {
52                                 if (blob_id(acl_func) != BLOBMSG_TYPE_STRING)
53                                         continue;
54
55                                 d = blobmsg_open_array(req, NULL);
56                                 blobmsg_add_string(req, NULL, blobmsg_name(acl_obj));
57                                 blobmsg_add_string(req, NULL, blobmsg_data(acl_func));
58                                 blobmsg_close_array(req, d);
59                         }
60                 }
61
62                 blobmsg_close_array(req, c);
63                 return true;
64         }
65         else if ((!strcmp(blobmsg_name(acl_scope), "uci") ||
66                   !strcmp(blobmsg_name(acl_scope), "luci-io")) &&
67                  blob_id(acl_scope) == BLOBMSG_TYPE_ARRAY)
68         {
69                 blobmsg_add_string(req, "scope", blobmsg_name(acl_scope));
70                 c = blobmsg_open_array(req, "objects");
71
72                 blobmsg_for_each_attr(acl_obj, acl_scope, rem)
73                 {
74                         if (blob_id(acl_obj) != BLOBMSG_TYPE_STRING)
75                                 continue;
76
77                         d = blobmsg_open_array(req, NULL);
78                         blobmsg_add_string(req, NULL, blobmsg_data(acl_obj));
79                         blobmsg_add_string(req, NULL, blobmsg_name(acl_perm));
80                         blobmsg_close_array(req, d);
81                 }
82
83                 blobmsg_close_array(req, c);
84                 return true;
85         }
86
87         return false;
88 }
89
90 static struct uci_section *
91 test_user_access(struct uci_context *uci, const char *user)
92 {
93         struct uci_package *p;
94         struct uci_section *s;
95         struct uci_element *e;
96         struct uci_ptr ptr = { .package = "luci", .option = "user" };
97
98         uci_load(uci, ptr.package, &p);
99
100         if (!p)
101                 return false;
102
103         uci_foreach_element(&p->sections, e)
104         {
105                 s = uci_to_section(e);
106
107                 if (strcmp(s->type, "access"))
108                         continue;
109
110                 ptr.section = s->e.name;
111                 ptr.s = NULL;
112                 ptr.o = NULL;
113
114                 if (uci_lookup_ptr(uci, &ptr, NULL, true))
115                         continue;
116
117                 if (ptr.o->type != UCI_TYPE_STRING)
118                         continue;
119
120                 if (strcmp(ptr.o->v.string, user))
121                         continue;
122
123                 return ptr.s;
124         }
125
126         return NULL;
127 }
128
129 static bool
130 test_group_access(struct uci_section *s, const char *perm, const char *group)
131 {
132         struct uci_option *o;
133         struct uci_element *e, *l;
134
135         uci_foreach_element(&s->options, e)
136         {
137                 o = uci_to_option(e);
138
139                 if (o->type != UCI_TYPE_LIST)
140                         continue;
141
142                 if (strcmp(o->e.name, perm))
143                         continue;
144
145                 uci_foreach_element(&o->v.list, l)
146                         if (l->name && !fnmatch(l->name, group, 0))
147                                 return true;
148         }
149
150         if (!strcmp(perm, "read"))
151                 return test_group_access(s, "write", group);
152
153         return false;
154 }
155
156 static void
157 setup_session_cb(struct ubus_request *req, int type, struct blob_attr *msg)
158 {
159         struct blob_attr *tb[__SES_NEW_MAX];
160         const char **sid = req->priv;
161
162         if (!msg)
163                 return;
164
165         blobmsg_parse(ses_new_policy, __SES_NEW_MAX, tb,
166                       blob_data(msg), blob_len(msg));
167
168         if (tb[SES_NEW_SID])
169                 *sid = strdup(blobmsg_data(tb[SES_NEW_SID]));
170 }
171
172 const char *
173 setup_session(const char *user)
174 {
175         uint32_t id;
176         struct blob_buf req = { 0 }, acl = { 0 };
177         struct blob_attr *acl_group, *acl_perm, *acl_scope;
178         struct ubus_context *ctx = NULL;
179         struct uci_context *uci = NULL;
180         struct uci_section *access;
181         const char *sid = NULL;
182         int i, rem, rem2, rem3;
183         void *c, *d;
184         glob_t gl;
185
186         uci = uci_alloc_context();
187
188         if (!uci)
189                 goto out;
190
191         access = test_user_access(uci, user);
192
193         /* continue without access group if user is root (backward compat) */
194         if (!access && strcmp(user, "root"))
195                 goto out;
196
197         ctx = ubus_connect(NULL);
198
199         if (!ctx || ubus_lookup_id(ctx, "session", &id))
200                 goto out;
201
202         blob_buf_init(&req, 0);
203         blobmsg_add_u32(&req, "timeout", 3600);
204         ubus_invoke(ctx, id, "create", req.head, setup_session_cb, &sid, 500);
205
206         if (!sid)
207                 goto out;
208
209         blob_buf_init(&req, 0);
210         blobmsg_add_string(&req, "sid", sid);
211         c = blobmsg_open_table(&req, "values");
212         blobmsg_add_string(&req, "user", user);
213         blobmsg_close_table(&req, c);
214         ubus_invoke(ctx, id, "set", req.head, NULL, NULL, 500);
215
216         if (glob("/usr/share/luci2/acl.d/*.json", 0, NULL, &gl))
217                 goto out;
218
219         for (i = 0; i < gl.gl_pathc; i++)
220         {
221                 blob_buf_init(&acl, 0);
222
223                 if (!blobmsg_add_json_from_file(&acl, gl.gl_pathv[i]))
224                 {
225                         fprintf(stderr, "Failed to parse %s\n", gl.gl_pathv[i]);
226                         continue;
227                 }
228
229                 blob_for_each_attr(acl_group, acl.head, rem)
230                 {
231                         blobmsg_for_each_attr(acl_perm, acl_group, rem2)
232                         {
233                                 if (blob_id(acl_perm) != BLOBMSG_TYPE_TABLE)
234                                         continue;
235
236                                 if (strcmp(blobmsg_name(acl_perm), "read") &&
237                                     strcmp(blobmsg_name(acl_perm), "write"))
238                                         continue;
239
240                                 if (access != NULL &&
241                                     !test_group_access(access, blobmsg_name(acl_perm),
242                                                        blobmsg_name(acl_group)))
243                                         continue;
244
245                                 blobmsg_for_each_attr(acl_scope, acl_perm, rem3)
246                                 {
247                                         blob_buf_init(&req, 0);
248
249                                         if (!parse_acl_scope(acl_perm, acl_scope, &req))
250                                                 continue;
251
252                                         blobmsg_add_string(&req, "sid", sid);
253                                         ubus_invoke(ctx, id, "grant", req.head, NULL, NULL, 500);
254
255
256                                         blob_buf_init(&req, 0);
257                                         blobmsg_add_string(&req, "sid", sid);
258                                         blobmsg_add_string(&req, "scope", "luci-ui");
259                                         c = blobmsg_open_array(&req, "objects");
260                                         d = blobmsg_open_array(&req, NULL);
261                                         blobmsg_add_string(&req, NULL, blobmsg_name(acl_group));
262                                         blobmsg_add_string(&req, NULL, blobmsg_name(acl_perm));
263                                         blobmsg_close_array(&req, d);
264                                         blobmsg_close_array(&req, c);
265                                         ubus_invoke(ctx, id, "grant", req.head, NULL, NULL, 500);
266                                 }
267                         }
268                 }
269         }
270
271         globfree(&gl);
272
273 out:
274         if (uci)
275                 uci_free_context(uci);
276
277         if (ctx)
278                 ubus_free(ctx);
279
280         return sid;
281 }