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