• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ubus/ubusd_acl.c

  1 /*
  2  * Copyright (C) 2015 John Crispin <blogic@openwrt.org>
  3  * Copyright (C) 2018 Hans Dedecker <dedeckeh@gmail.com>
  4  *
  5  * This program is free software; you can redistribute it and/or modify
  6  * it under the terms of the GNU Lesser General Public License version 2.1
  7  * as published by the Free Software Foundation
  8  *
  9  * This program is distributed in the hope that it will be useful,
 10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  * GNU General Public License for more details.
 13  */
 14 
 15 #define _GNU_SOURCE
 16 #include <sys/socket.h>
 17 #include <sys/types.h>
 18 #include <sys/stat.h>
 19 
 20 #include <syslog.h>
 21 #include <unistd.h>
 22 #include <glob.h>
 23 #include <grp.h>
 24 #include <pwd.h>
 25 
 26 #include <libubox/vlist.h>
 27 #include <libubox/blobmsg_json.h>
 28 #include <libubox/avl-cmp.h>
 29 
 30 #include "ubusd.h"
 31 
 32 #ifndef SO_PEERCRED
 33 struct ucred {
 34         int pid;
 35         int uid;
 36         int gid;
 37 };
 38 #endif
 39 
 40 struct ubusd_acl_obj {
 41         struct avl_node avl;
 42         struct list_head list;
 43 
 44         bool partial;
 45 
 46         const char *user;
 47         const char *group;
 48 
 49         struct blob_attr *methods;
 50         struct blob_attr *tags;
 51         struct blob_attr *priv;
 52         bool subscribe;
 53         bool publish;
 54         bool listen;
 55         bool send;
 56 };
 57 
 58 struct ubusd_acl_file {
 59         struct vlist_node avl;
 60 
 61         const char *user;
 62         const char *group;
 63 
 64         struct blob_attr *blob;
 65         struct list_head acl;
 66 
 67         int ok;
 68 };
 69 
 70 const char *ubusd_acl_dir = "/usr/share/acl.d";
 71 static struct blob_buf bbuf;
 72 static struct avl_tree ubusd_acls;
 73 static int ubusd_acl_seq;
 74 static struct ubus_object *acl_obj;
 75 
 76 static int
 77 ubusd_acl_match_cred(struct ubus_client *cl, struct ubusd_acl_obj *obj)
 78 {
 79         if (obj->user && !strcmp(cl->user, obj->user))
 80                 return 0;
 81 
 82         if (obj->group && !strcmp(cl->group, obj->group))
 83                 return 0;
 84 
 85         return -1;
 86 }
 87 
 88 int
 89 ubusd_acl_check(struct ubus_client *cl, const char *obj,
 90                 const char *method, enum ubusd_acl_type type)
 91 {
 92         struct ubusd_acl_obj *acl;
 93         int match_len = 0;
 94 
 95         if (!cl || !cl->uid || !obj)
 96                 return 0;
 97 
 98         /*
 99          * Since this tree is sorted alphabetically, we can only expect
100          * to find matching entries as long as the number of matching
101          * characters between the access list string and the object path
102          * is monotonically increasing.
103          */
104         avl_for_each_element(&ubusd_acls, acl, avl) {
105                 const char *key = acl->avl.key;
106                 int cur_match_len;
107                 bool full_match;
108 
109                 full_match = ubus_strmatch_len(obj, key, &cur_match_len);
110                 if (cur_match_len < match_len)
111                         break;
112 
113                 match_len = cur_match_len;
114 
115                 if (!full_match) {
116                         if (!acl->partial)
117                                 continue;
118 
119                         if (match_len != (int) strlen(key))
120                                 continue;
121                 }
122 
123                 if (ubusd_acl_match_cred(cl, acl))
124                         continue;
125 
126                 switch (type) {
127                 case UBUS_ACL_PUBLISH:
128                         if (acl->publish)
129                                 return 0;
130                         break;
131 
132                 case UBUS_ACL_SUBSCRIBE:
133                         if (acl->subscribe)
134                                 return 0;
135                         break;
136 
137                 case UBUS_ACL_LISTEN:
138                         if (acl->listen)
139                                 return 0;
140                         break;
141 
142                 case UBUS_ACL_SEND:
143                         if (acl->send)
144                                 return 0;
145                         break;
146 
147                 case UBUS_ACL_ACCESS:
148                         if (acl->methods) {
149                                 struct blob_attr *cur;
150                                 size_t rem;
151 
152                                 blobmsg_for_each_attr(cur, acl->methods, rem)
153                                         if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
154                                                 if (!strcmp(method, blobmsg_get_string(cur)))
155                                                         return 0;
156                         }
157                         break;
158                 }
159         }
160 
161         return -1;
162 }
163 
164 int
165 ubusd_acl_init_client(struct ubus_client *cl, int fd)
166 {
167         struct ucred cred;
168         struct passwd *pwd;
169         struct group *group;
170 
171 #ifdef SO_PEERCRED
172         unsigned int len = sizeof(struct ucred);
173 
174         if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &cred, &len) == -1)
175                 return -1;
176 #else
177         memset(&cred, 0, sizeof(cred));
178 #endif
179 
180         pwd = getpwuid(cred.uid);
181         if (!pwd)
182                 return -1;
183 
184         group = getgrgid(cred.gid);
185         if (!group)
186                 return -1;
187 
188         cl->uid = cred.uid;
189         cl->gid = cred.gid;
190 
191         cl->group = strdup(group->gr_name);
192         cl->user = strdup(pwd->pw_name);
193 
194         return 0;
195 }
196 
197 void
198 ubusd_acl_free_client(struct ubus_client *cl)
199 {
200         free(cl->group);
201         free(cl->user);
202 }
203 
204 static void
205 ubusd_acl_file_free(struct ubusd_acl_file *file)
206 {
207         struct ubusd_acl_obj *p, *q;
208 
209         list_for_each_entry_safe(p, q, &file->acl, list) {
210                 avl_delete(&ubusd_acls, &p->avl);
211                 list_del(&p->list);
212                 free(p);
213         }
214 
215         free(file);
216 }
217 
218 enum {
219         ACL_ACCESS_METHODS,
220         ACL_ACCESS_TAGS,
221         ACL_ACCESS_PRIV,
222         __ACL_ACCESS_MAX
223 };
224 
225 static const struct blobmsg_policy acl_obj_policy[__ACL_ACCESS_MAX] = {
226         [ACL_ACCESS_METHODS] = { .name = "methods", .type = BLOBMSG_TYPE_ARRAY },
227         [ACL_ACCESS_TAGS] = { .name = "tags", .type = BLOBMSG_TYPE_ARRAY },
228         [ACL_ACCESS_PRIV] = { .name = "acl", .type = BLOBMSG_TYPE_TABLE },
229 };
230 
231 static struct ubusd_acl_obj*
232 ubusd_acl_alloc_obj(struct ubusd_acl_file *file, const char *obj)
233 {
234         struct ubusd_acl_obj *o;
235         int len = strlen(obj);
236         char *k;
237         bool partial = false;
238 
239         if (obj[len - 1] == '*') {
240                 partial = true;
241                 len--;
242         }
243 
244         o = calloc_a(sizeof(*o), &k, len + 1);
245         o->partial = partial;
246         o->user = file->user;
247         o->group = file->group;
248         o->avl.key = memcpy(k, obj, len);
249 
250         list_add(&o->list, &file->acl);
251         avl_insert(&ubusd_acls, &o->avl);
252 
253         return o;
254 }
255 
256 static void
257 ubusd_acl_add_access(struct ubusd_acl_file *file, struct blob_attr *obj)
258 {
259         struct blob_attr *tb[__ACL_ACCESS_MAX];
260         struct ubusd_acl_obj *o;
261 
262         blobmsg_parse(acl_obj_policy, __ACL_ACCESS_MAX, tb, blobmsg_data(obj),
263                       blobmsg_data_len(obj));
264 
265         if (!tb[ACL_ACCESS_METHODS] && !tb[ACL_ACCESS_TAGS] && !tb[ACL_ACCESS_PRIV])
266                 return;
267 
268         o = ubusd_acl_alloc_obj(file, blobmsg_name(obj));
269 
270         o->methods = tb[ACL_ACCESS_METHODS];
271         o->tags = tb[ACL_ACCESS_TAGS];
272         o->priv = tb[ACL_ACCESS_PRIV];
273 
274         if (file->user || file->group)
275                 file->ok = 1;
276 }
277 
278 static void
279 ubusd_acl_add_subscribe(struct ubusd_acl_file *file, const char *obj)
280 {
281         struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
282 
283         o->subscribe = true;
284 }
285 
286 static void
287 ubusd_acl_add_publish(struct ubusd_acl_file *file, const char *obj)
288 {
289         struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
290 
291         o->publish = true;
292 }
293 
294 static void ubusd_acl_add_listen(struct ubusd_acl_file *file, const char *obj)
295 {
296         struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
297 
298         o->listen = true;
299 }
300 
301 static void ubusd_acl_add_send(struct ubusd_acl_file *file, const char *obj)
302 {
303         struct ubusd_acl_obj *o = ubusd_acl_alloc_obj(file, obj);
304 
305         o->send = true;
306 }
307 
308 enum {
309         ACL_USER,
310         ACL_GROUP,
311         ACL_ACCESS,
312         ACL_PUBLISH,
313         ACL_SUBSCRIBE,
314         ACL_INHERIT,
315         ACL_LISTEN,
316         ACL_SEND,
317         __ACL_MAX
318 };
319 
320 static const struct blobmsg_policy acl_policy[__ACL_MAX] = {
321         [ACL_USER] = { .name = "user", .type = BLOBMSG_TYPE_STRING },
322         [ACL_GROUP] = { .name = "group", .type = BLOBMSG_TYPE_STRING },
323         [ACL_ACCESS] = { .name = "access", .type = BLOBMSG_TYPE_TABLE },
324         [ACL_PUBLISH] = { .name = "publish", .type = BLOBMSG_TYPE_ARRAY },
325         [ACL_SUBSCRIBE] = { .name = "subscribe", .type = BLOBMSG_TYPE_ARRAY },
326         [ACL_INHERIT] = { .name = "inherit", .type = BLOBMSG_TYPE_ARRAY },
327         [ACL_LISTEN] = { .name= "listen", .type = BLOBMSG_TYPE_ARRAY },
328         [ACL_SEND] = { .name= "send", .type = BLOBMSG_TYPE_ARRAY },
329 };
330 
331 static void
332 ubusd_acl_file_add(struct ubusd_acl_file *file)
333 {
334         struct blob_attr *tb[__ACL_MAX], *cur;
335         size_t rem;
336 
337         blobmsg_parse(acl_policy, __ACL_MAX, tb, blob_data(file->blob),
338                       blob_len(file->blob));
339 
340         if (tb[ACL_USER])
341                 file->user = blobmsg_get_string(tb[ACL_USER]);
342         else if (tb[ACL_GROUP])
343                 file->group = blobmsg_get_string(tb[ACL_GROUP]);
344         else
345                 return;
346 
347         if (tb[ACL_ACCESS])
348                 blobmsg_for_each_attr(cur, tb[ACL_ACCESS], rem)
349                         ubusd_acl_add_access(file, cur);
350 
351         if (tb[ACL_SUBSCRIBE])
352                 blobmsg_for_each_attr(cur, tb[ACL_SUBSCRIBE], rem)
353                         if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
354                                 ubusd_acl_add_subscribe(file, blobmsg_get_string(cur));
355 
356         if (tb[ACL_PUBLISH])
357                 blobmsg_for_each_attr(cur, tb[ACL_PUBLISH], rem)
358                         if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
359                                 ubusd_acl_add_publish(file, blobmsg_get_string(cur));
360 
361         if (tb[ACL_LISTEN])
362                 blobmsg_for_each_attr(cur, tb[ACL_LISTEN], rem)
363                         if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
364                                 ubusd_acl_add_listen(file, blobmsg_get_string(cur));
365 
366         if (tb[ACL_SEND])
367                 blobmsg_for_each_attr(cur, tb[ACL_SEND], rem)
368                         if (blobmsg_type(cur) == BLOBMSG_TYPE_STRING)
369                                 ubusd_acl_add_send(file, blobmsg_get_string(cur));
370 }
371 
372 static void
373 ubusd_acl_update_cb(struct vlist_tree *tree, struct vlist_node *node_new,
374         struct vlist_node *node_old)
375 {
376         struct ubusd_acl_file *file;
377 
378         if (node_old) {
379                 file = container_of(node_old, struct ubusd_acl_file, avl);
380                 ubusd_acl_file_free(file);
381         }
382 
383         if (node_new) {
384                 file = container_of(node_new, struct ubusd_acl_file, avl);
385                 ubusd_acl_file_add(file);
386         }
387 }
388 
389 static struct ubus_msg_buf *
390 ubusd_create_sequence_event_msg(void *priv, const char *id)
391 {
392         void *s;
393 
394         blob_buf_init(&b, 0);
395         blob_put_int32(&b, UBUS_ATTR_OBJID, 0);
396         blob_put_string(&b, UBUS_ATTR_METHOD, id);
397         s = blob_nest_start(&b, UBUS_ATTR_DATA);
398         blobmsg_add_u32(&b, "sequence", ubusd_acl_seq);
399         blob_nest_end(&b, s);
400 
401         return ubus_msg_new(b.head, blob_raw_len(b.head), true);
402 }
403 
404 static VLIST_TREE(ubusd_acl_files, avl_strcmp, ubusd_acl_update_cb, false, false);
405 
406 static int
407 ubusd_acl_load_file(const char *filename)
408 {
409         struct ubusd_acl_file *file;
410         void *blob;
411 
412         blob_buf_init(&bbuf, 0);
413         if (!blobmsg_add_json_from_file(&bbuf, filename)) {
414                 syslog(LOG_ERR, "failed to parse %s\n", filename);
415                 return -1;
416         }
417 
418         file = calloc_a(sizeof(*file), &blob, blob_raw_len(bbuf.head));
419         if (!file)
420                 return -1;
421 
422         file->blob = blob;
423 
424         memcpy(blob, bbuf.head, blob_raw_len(bbuf.head));
425         INIT_LIST_HEAD(&file->acl);
426 
427         vlist_add(&ubusd_acl_files, &file->avl, filename);
428         syslog(LOG_INFO, "loading %s\n", filename);
429 
430         return 0;
431 }
432 
433 void
434 ubusd_acl_load(void)
435 {
436         struct stat st;
437         glob_t gl;
438         size_t j;
439         const char *suffix = "/*.json";
440         char *path = alloca(strlen(ubusd_acl_dir) + strlen(suffix) + 1);
441 
442         sprintf(path, "%s%s", ubusd_acl_dir, suffix);
443         if (glob(path, GLOB_NOESCAPE | GLOB_MARK, NULL, &gl))
444                 return;
445 
446         vlist_update(&ubusd_acl_files);
447         for (j = 0; j < gl.gl_pathc; j++) {
448                 if (stat(gl.gl_pathv[j], &st) || !S_ISREG(st.st_mode))
449                         continue;
450 
451                 if (st.st_uid || st.st_gid) {
452                         syslog(LOG_ERR, "%s has wrong owner\n", gl.gl_pathv[j]);
453                         continue;
454                 }
455                 if (st.st_mode & (S_IWOTH | S_IWGRP | S_IXOTH)) {
456                         syslog(LOG_ERR, "%s has wrong permissions\n", gl.gl_pathv[j]);
457                         continue;
458                 }
459                 ubusd_acl_load_file(gl.gl_pathv[j]);
460         }
461 
462         globfree(&gl);
463         vlist_flush(&ubusd_acl_files);
464         ubusd_acl_seq++;
465         ubusd_send_event(NULL, "ubus.acl.sequence", ubusd_create_sequence_event_msg, NULL);
466 }
467 
468 static void
469 ubusd_reply_add(struct ubus_object *obj)
470 {
471         struct ubusd_acl_obj *acl;
472         int match_len = 0;
473 
474         if (!obj->path.key)
475                 return;
476 
477         /*
478          * Since this tree is sorted alphabetically, we can only expect
479          * to find matching entries as long as the number of matching
480          * characters between the access list string and the object path
481          * is monotonically increasing.
482          */
483         avl_for_each_element(&ubusd_acls, acl, avl) {
484                 const char *key = acl->avl.key;
485                 int cur_match_len;
486                 bool full_match;
487                 void *c;
488 
489                 if (!acl->priv)
490                         continue;
491 
492                 full_match = ubus_strmatch_len(obj->path.key, key, &cur_match_len);
493                 if (cur_match_len < match_len)
494                         break;
495 
496                 match_len = cur_match_len;
497 
498                 if (!full_match) {
499                         if (!acl->partial)
500                                 continue;
501 
502                         if (match_len != (int) strlen(key))
503                                 continue;
504                 }
505 
506                 c = blobmsg_open_table(&b, NULL);
507                 blobmsg_add_string(&b, "obj", obj->path.key);
508                 if (acl->user)
509                         blobmsg_add_string(&b, "user", acl->user);
510                 if (acl->group)
511                         blobmsg_add_string(&b, "group", acl->group);
512 
513                 blobmsg_add_field(&b, blobmsg_type(acl->priv), "acl",
514                         blobmsg_data(acl->priv), blobmsg_data_len(acl->priv));
515 
516                 blobmsg_close_table(&b, c);
517         }
518 }
519 
520 static int ubusd_reply_query(struct ubus_client *cl, struct ubus_msg_buf *ub, struct blob_attr **attr, struct blob_attr *msg)
521 {
522         struct ubus_object *obj;
523         void *d, *a;
524 
525         if (!attr[UBUS_ATTR_OBJID])
526                 return UBUS_STATUS_INVALID_ARGUMENT;
527 
528         obj = ubusd_find_object(blob_get_u32(attr[UBUS_ATTR_OBJID]));
529         if (!obj)
530                 return UBUS_STATUS_NOT_FOUND;
531 
532         blob_buf_init(&b, 0);
533         blob_put_int32(&b, UBUS_ATTR_OBJID, obj->id.id);
534         d = blob_nest_start(&b, UBUS_ATTR_DATA);
535 
536         blobmsg_add_u32(&b, "seq", ubusd_acl_seq);
537         a = blobmsg_open_array(&b, "acl");
538         list_for_each_entry(obj, &cl->objects, list)
539                 ubusd_reply_add(obj);
540         blobmsg_close_table(&b, a);
541 
542         blob_nest_end(&b, d);
543 
544         ubus_proto_send_msg_from_blob(cl, ub, UBUS_MSG_DATA);
545 
546         return 0;
547 }
548 
549 static int ubusd_acl_recv(struct ubus_client *cl, struct ubus_msg_buf *ub, const char *method, struct blob_attr *msg)
550 {
551         if (!strcmp(method, "query"))
552                 return ubusd_reply_query(cl, ub, ubus_parse_msg(ub->data, blob_raw_len(ub->data)), msg);
553 
554         return UBUS_STATUS_INVALID_COMMAND;
555 }
556 
557 void ubusd_acl_init(void)
558 {
559         ubus_init_string_tree(&ubusd_acls, true);
560         acl_obj = ubusd_create_object_internal(NULL, UBUS_SYSTEM_OBJECT_ACL);
561         acl_obj->recv_msg = ubusd_acl_recv;
562 }
563 

This page was automatically generated by LXR 0.3.1.  •  OpenWrt