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

This page was automatically generated by LXR 0.3.1.  •  OpenWrt