Initial commit of LuCI2
[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 }