1 /* 2 * rpcd - UBUS RPC server 3 * 4 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org> 5 * Copyright (C) 2013-2014 Jo-Philipp Wich <jow@openwrt.org> 6 * 7 * Permission to use, copy, modify, and/or distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #define _GNU_SOURCE /* crypt() */ 21 22 #include <libubox/avl-cmp.h> 23 #include <libubox/blobmsg.h> 24 #include <libubox/utils.h> 25 #include <libubus.h> 26 #include <fnmatch.h> 27 #include <glob.h> 28 #include <uci.h> 29 #include <limits.h> 30 31 #ifdef HAVE_SHADOW 32 #include <shadow.h> 33 #endif 34 35 #include <rpcd/session.h> 36 37 static struct avl_tree sessions; 38 static struct blob_buf buf; 39 40 static LIST_HEAD(create_callbacks); 41 static LIST_HEAD(destroy_callbacks); 42 43 enum { 44 RPC_SN_TIMEOUT, 45 __RPC_SN_MAX, 46 }; 47 static const struct blobmsg_policy new_policy[__RPC_SN_MAX] = { 48 [RPC_SN_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 }, 49 }; 50 51 enum { 52 RPC_SI_SID, 53 __RPC_SI_MAX, 54 }; 55 static const struct blobmsg_policy sid_policy[__RPC_SI_MAX] = { 56 [RPC_SI_SID] = { .name = "ubus_rpc_session", .type = BLOBMSG_TYPE_STRING }, 57 }; 58 59 enum { 60 RPC_SS_SID, 61 RPC_SS_VALUES, 62 __RPC_SS_MAX, 63 }; 64 static const struct blobmsg_policy set_policy[__RPC_SS_MAX] = { 65 [RPC_SS_SID] = { .name = "ubus_rpc_session", .type = BLOBMSG_TYPE_STRING }, 66 [RPC_SS_VALUES] = { .name = "values", .type = BLOBMSG_TYPE_TABLE }, 67 }; 68 69 enum { 70 RPC_SG_SID, 71 RPC_SG_KEYS, 72 __RPC_SG_MAX, 73 }; 74 static const struct blobmsg_policy get_policy[__RPC_SG_MAX] = { 75 [RPC_SG_SID] = { .name = "ubus_rpc_session", .type = BLOBMSG_TYPE_STRING }, 76 [RPC_SG_KEYS] = { .name = "keys", .type = BLOBMSG_TYPE_ARRAY }, 77 }; 78 79 enum { 80 RPC_SA_SID, 81 RPC_SA_SCOPE, 82 RPC_SA_OBJECTS, 83 __RPC_SA_MAX, 84 }; 85 static const struct blobmsg_policy acl_policy[__RPC_SA_MAX] = { 86 [RPC_SA_SID] = { .name = "ubus_rpc_session", .type = BLOBMSG_TYPE_STRING }, 87 [RPC_SA_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING }, 88 [RPC_SA_OBJECTS] = { .name = "objects", .type = BLOBMSG_TYPE_ARRAY }, 89 }; 90 91 enum { 92 RPC_SP_SID, 93 RPC_SP_SCOPE, 94 RPC_SP_OBJECT, 95 RPC_SP_FUNCTION, 96 __RPC_SP_MAX, 97 }; 98 static const struct blobmsg_policy perm_policy[__RPC_SP_MAX] = { 99 [RPC_SP_SID] = { .name = "ubus_rpc_session", .type = BLOBMSG_TYPE_STRING }, 100 [RPC_SP_SCOPE] = { .name = "scope", .type = BLOBMSG_TYPE_STRING }, 101 [RPC_SP_OBJECT] = { .name = "object", .type = BLOBMSG_TYPE_STRING }, 102 [RPC_SP_FUNCTION] = { .name = "function", .type = BLOBMSG_TYPE_STRING }, 103 }; 104 105 enum { 106 RPC_DUMP_SID, 107 RPC_DUMP_TIMEOUT, 108 RPC_DUMP_EXPIRES, 109 RPC_DUMP_DATA, 110 __RPC_DUMP_MAX, 111 }; 112 static const struct blobmsg_policy dump_policy[__RPC_DUMP_MAX] = { 113 [RPC_DUMP_SID] = { .name = "ubus_rpc_session", .type = BLOBMSG_TYPE_STRING }, 114 [RPC_DUMP_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 }, 115 [RPC_DUMP_EXPIRES] = { .name = "expires", .type = BLOBMSG_TYPE_INT64 }, 116 [RPC_DUMP_DATA] = { .name = "data", .type = BLOBMSG_TYPE_TABLE }, 117 }; 118 119 enum { 120 RPC_L_USERNAME, 121 RPC_L_PASSWORD, 122 RPC_L_TIMEOUT, 123 __RPC_L_MAX, 124 }; 125 static const struct blobmsg_policy login_policy[__RPC_L_MAX] = { 126 [RPC_L_USERNAME] = { .name = "username", .type = BLOBMSG_TYPE_STRING }, 127 [RPC_L_PASSWORD] = { .name = "password", .type = BLOBMSG_TYPE_STRING }, 128 [RPC_L_TIMEOUT] = { .name = "timeout", .type = BLOBMSG_TYPE_INT32 }, 129 }; 130 131 /* 132 * Keys in the AVL tree contain all pattern characters up to the first wildcard. 133 * To look up entries, start with the last entry that has a key less than or 134 * equal to the method name, then work backwards as long as the AVL key still 135 * matches its counterpart in the object name 136 */ 137 #define uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \ 138 for (_acl = avl_find_le_element(_avl, _obj, _acl, avl); \ 139 _acl; \ 140 _acl = avl_is_first(_avl, &(_acl)->avl) ? NULL : \ 141 avl_prev_element((_acl), avl)) 142 143 #define uh_foreach_matching_acl(_acl, _avl, _obj, _func) \ 144 uh_foreach_matching_acl_prefix(_acl, _avl, _obj, _func) \ 145 if (!strncmp((_acl)->object, _obj, (_acl)->sort_len) && \ 146 !fnmatch((_acl)->object, (_obj), FNM_NOESCAPE) && \ 147 !fnmatch((_acl)->function, (_func), FNM_NOESCAPE)) 148 149 static int 150 rpc_random(char *dest) 151 { 152 unsigned char buf[16] = { 0 }; 153 FILE *f; 154 int i; 155 int ret; 156 157 f = fopen("/dev/urandom", "r"); 158 if (!f) 159 return -1; 160 161 ret = fread(buf, 1, sizeof(buf), f); 162 fclose(f); 163 164 if (ret < 0) 165 return ret; 166 167 for (i = 0; i < sizeof(buf); i++) 168 sprintf(dest + (i<<1), "%02x", buf[i]); 169 170 return 0; 171 } 172 173 static void 174 rpc_session_dump_data(struct rpc_session *ses, struct blob_buf *b) 175 { 176 struct rpc_session_data *d; 177 178 avl_for_each_element(&ses->data, d, avl) { 179 blobmsg_add_field(b, blobmsg_type(d->attr), blobmsg_name(d->attr), 180 blobmsg_data(d->attr), blobmsg_data_len(d->attr)); 181 } 182 } 183 184 static void 185 rpc_session_dump_acls(struct rpc_session *ses, struct blob_buf *b) 186 { 187 struct rpc_session_acl *acl; 188 struct rpc_session_acl_scope *acl_scope; 189 const char *lastobj = NULL; 190 const char *lastscope = NULL; 191 void *c = NULL, *d = NULL; 192 193 avl_for_each_element(&ses->acls, acl_scope, avl) { 194 if (!lastscope || strcmp(acl_scope->avl.key, lastscope)) 195 { 196 if (c) blobmsg_close_table(b, c); 197 c = blobmsg_open_table(b, acl_scope->avl.key); 198 lastobj = NULL; 199 } 200 201 d = NULL; 202 203 avl_for_each_element(&acl_scope->acls, acl, avl) { 204 if (!lastobj || strcmp(acl->object, lastobj)) 205 { 206 if (d) blobmsg_close_array(b, d); 207 d = blobmsg_open_array(b, acl->object); 208 } 209 210 blobmsg_add_string(b, NULL, acl->function); 211 lastobj = acl->object; 212 } 213 214 if (d) blobmsg_close_array(b, d); 215 } 216 217 if (c) blobmsg_close_table(b, c); 218 } 219 220 static void 221 rpc_session_to_blob(struct rpc_session *ses, bool acls) 222 { 223 void *c; 224 225 blob_buf_init(&buf, 0); 226 227 blobmsg_add_string(&buf, "ubus_rpc_session", ses->id); 228 blobmsg_add_u32(&buf, "timeout", ses->timeout); 229 blobmsg_add_u64(&buf, "expires", uloop_timeout_remaining64(&ses->t) / 1000); 230 231 if (acls) { 232 c = blobmsg_open_table(&buf, "acls"); 233 rpc_session_dump_acls(ses, &buf); 234 blobmsg_close_table(&buf, c); 235 } 236 237 c = blobmsg_open_table(&buf, "data"); 238 rpc_session_dump_data(ses, &buf); 239 blobmsg_close_table(&buf, c); 240 } 241 242 static void 243 rpc_session_dump(struct rpc_session *ses, struct ubus_context *ctx, 244 struct ubus_request_data *req) 245 { 246 rpc_session_to_blob(ses, true); 247 248 ubus_send_reply(ctx, req, buf.head); 249 } 250 251 static void 252 rpc_touch_session(struct rpc_session *ses) 253 { 254 if (ses->timeout > 0) 255 uloop_timeout_set(&ses->t, ses->timeout * 1000); 256 } 257 258 static void 259 rpc_session_destroy(struct rpc_session *ses) 260 { 261 struct rpc_session_acl *acl, *nacl; 262 struct rpc_session_acl_scope *acl_scope, *nacl_scope; 263 struct rpc_session_data *data, *ndata; 264 struct rpc_session_cb *cb; 265 266 list_for_each_entry(cb, &destroy_callbacks, list) 267 cb->cb(ses, cb->priv); 268 269 uloop_timeout_cancel(&ses->t); 270 271 avl_for_each_element_safe(&ses->acls, acl_scope, avl, nacl_scope) { 272 avl_remove_all_elements(&acl_scope->acls, acl, avl, nacl) 273 free(acl); 274 275 avl_delete(&ses->acls, &acl_scope->avl); 276 free(acl_scope); 277 } 278 279 avl_remove_all_elements(&ses->data, data, avl, ndata) 280 free(data); 281 282 avl_delete(&sessions, &ses->avl); 283 free(ses); 284 } 285 286 static void rpc_session_timeout(struct uloop_timeout *t) 287 { 288 struct rpc_session *ses; 289 290 ses = container_of(t, struct rpc_session, t); 291 rpc_session_destroy(ses); 292 } 293 294 static struct rpc_session * 295 rpc_session_new(void) 296 { 297 struct rpc_session *ses; 298 299 ses = calloc(1, sizeof(*ses)); 300 301 if (!ses) 302 return NULL; 303 304 ses->avl.key = ses->id; 305 306 avl_init(&ses->acls, avl_strcmp, true, NULL); 307 avl_init(&ses->data, avl_strcmp, false, NULL); 308 309 ses->t.cb = rpc_session_timeout; 310 311 return ses; 312 } 313 314 static struct rpc_session * 315 rpc_session_create(int timeout) 316 { 317 struct rpc_session *ses; 318 struct rpc_session_cb *cb; 319 320 ses = rpc_session_new(); 321 322 if (!ses) 323 return NULL; 324 325 if (rpc_random(ses->id)) 326 return NULL; 327 328 ses->timeout = timeout; 329 330 avl_insert(&sessions, &ses->avl); 331 332 rpc_touch_session(ses); 333 334 list_for_each_entry(cb, &create_callbacks, list) 335 cb->cb(ses, cb->priv); 336 337 return ses; 338 } 339 340 static struct rpc_session * 341 rpc_session_get(const char *id) 342 { 343 struct rpc_session *ses; 344 345 ses = avl_find_element(&sessions, id, ses, avl); 346 if (!ses) 347 return NULL; 348 349 rpc_touch_session(ses); 350 return ses; 351 } 352 353 static int 354 rpc_handle_create(struct ubus_context *ctx, struct ubus_object *obj, 355 struct ubus_request_data *req, const char *method, 356 struct blob_attr *msg) 357 { 358 struct rpc_session *ses; 359 struct blob_attr *tb; 360 int timeout = RPC_DEFAULT_SESSION_TIMEOUT; 361 362 blobmsg_parse(new_policy, __RPC_SN_MAX, &tb, blob_data(msg), blob_len(msg)); 363 if (tb) 364 timeout = blobmsg_get_u32(tb); 365 366 ses = rpc_session_create(timeout); 367 if (ses) 368 rpc_session_dump(ses, ctx, req); 369 370 return 0; 371 } 372 373 static int 374 rpc_handle_list(struct ubus_context *ctx, struct ubus_object *obj, 375 struct ubus_request_data *req, const char *method, 376 struct blob_attr *msg) 377 { 378 struct rpc_session *ses; 379 struct blob_attr *tb; 380 381 blobmsg_parse(sid_policy, __RPC_SI_MAX, &tb, blob_data(msg), blob_len(msg)); 382 383 if (!tb) { 384 avl_for_each_element(&sessions, ses, avl) 385 rpc_session_dump(ses, ctx, req); 386 return 0; 387 } 388 389 ses = rpc_session_get(blobmsg_data(tb)); 390 if (!ses) 391 return UBUS_STATUS_NOT_FOUND; 392 393 rpc_session_dump(ses, ctx, req); 394 395 return 0; 396 } 397 398 static int 399 uh_id_len(const char *str) 400 { 401 return strcspn(str, "*?["); 402 } 403 404 static int 405 rpc_session_grant(struct rpc_session *ses, 406 const char *scope, const char *object, const char *function) 407 { 408 struct rpc_session_acl *acl; 409 struct rpc_session_acl_scope *acl_scope; 410 char *new_scope, *new_obj, *new_func, *new_id; 411 int id_len; 412 413 if (!object || !function) 414 return UBUS_STATUS_INVALID_ARGUMENT; 415 416 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl); 417 418 if (acl_scope) { 419 uh_foreach_matching_acl_prefix(acl, &acl_scope->acls, object, function) { 420 if (!strcmp(acl->object, object) && 421 !strcmp(acl->function, function)) 422 return 0; 423 } 424 } 425 426 if (!acl_scope) { 427 acl_scope = calloc_a(sizeof(*acl_scope), 428 &new_scope, strlen(scope) + 1); 429 430 if (!acl_scope) 431 return UBUS_STATUS_UNKNOWN_ERROR; 432 433 acl_scope->avl.key = strcpy(new_scope, scope); 434 avl_init(&acl_scope->acls, avl_strcmp, true, NULL); 435 avl_insert(&ses->acls, &acl_scope->avl); 436 } 437 438 id_len = uh_id_len(object); 439 acl = calloc_a(sizeof(*acl), 440 &new_obj, strlen(object) + 1, 441 &new_func, strlen(function) + 1, 442 &new_id, id_len + 1); 443 444 if (!acl) 445 return UBUS_STATUS_UNKNOWN_ERROR; 446 447 acl->object = strcpy(new_obj, object); 448 acl->function = strcpy(new_func, function); 449 acl->avl.key = strncpy(new_id, object, id_len); 450 avl_insert(&acl_scope->acls, &acl->avl); 451 452 return 0; 453 } 454 455 static int 456 rpc_session_revoke(struct rpc_session *ses, 457 const char *scope, const char *object, const char *function) 458 { 459 struct rpc_session_acl *acl, *next; 460 struct rpc_session_acl_scope *acl_scope; 461 int id_len; 462 char *id; 463 464 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl); 465 466 if (!acl_scope) 467 return 0; 468 469 if (!object && !function) { 470 avl_remove_all_elements(&acl_scope->acls, acl, avl, next) 471 free(acl); 472 avl_delete(&ses->acls, &acl_scope->avl); 473 free(acl_scope); 474 return 0; 475 } 476 477 id_len = uh_id_len(object); 478 id = alloca(id_len + 1); 479 strncpy(id, object, id_len); 480 id[id_len] = 0; 481 482 acl = avl_find_element(&acl_scope->acls, id, acl, avl); 483 while (acl) { 484 if (!avl_is_last(&acl_scope->acls, &acl->avl)) 485 next = avl_next_element(acl, avl); 486 else 487 next = NULL; 488 489 if (strcmp(id, acl->avl.key) != 0) 490 break; 491 492 if (!strcmp(acl->object, object) && 493 !strcmp(acl->function, function)) { 494 avl_delete(&acl_scope->acls, &acl->avl); 495 free(acl); 496 } 497 acl = next; 498 } 499 500 if (avl_is_empty(&acl_scope->acls)) { 501 avl_delete(&ses->acls, &acl_scope->avl); 502 free(acl_scope); 503 } 504 505 return 0; 506 } 507 508 509 static int 510 rpc_handle_acl(struct ubus_context *ctx, struct ubus_object *obj, 511 struct ubus_request_data *req, const char *method, 512 struct blob_attr *msg) 513 { 514 struct rpc_session *ses; 515 struct blob_attr *tb[__RPC_SA_MAX]; 516 struct blob_attr *attr, *sattr; 517 const char *object, *function; 518 const char *scope = "ubus"; 519 int rem1, rem2; 520 521 int (*cb)(struct rpc_session *ses, 522 const char *scope, const char *object, const char *function); 523 524 blobmsg_parse(acl_policy, __RPC_SA_MAX, tb, blob_data(msg), blob_len(msg)); 525 526 if (!tb[RPC_SA_SID]) 527 return UBUS_STATUS_INVALID_ARGUMENT; 528 529 ses = rpc_session_get(blobmsg_data(tb[RPC_SA_SID])); 530 if (!ses) 531 return UBUS_STATUS_NOT_FOUND; 532 533 if (tb[RPC_SA_SCOPE]) 534 scope = blobmsg_data(tb[RPC_SA_SCOPE]); 535 536 if (!strcmp(method, "grant")) 537 cb = rpc_session_grant; 538 else 539 cb = rpc_session_revoke; 540 541 if (!tb[RPC_SA_OBJECTS]) 542 return cb(ses, scope, NULL, NULL); 543 544 blobmsg_for_each_attr(attr, tb[RPC_SA_OBJECTS], rem1) { 545 if (blobmsg_type(attr) != BLOBMSG_TYPE_ARRAY) 546 continue; 547 548 object = NULL; 549 function = NULL; 550 551 blobmsg_for_each_attr(sattr, attr, rem2) { 552 if (blobmsg_type(sattr) != BLOBMSG_TYPE_STRING) 553 continue; 554 555 if (!object) 556 object = blobmsg_data(sattr); 557 else if (!function) 558 function = blobmsg_data(sattr); 559 else 560 break; 561 } 562 563 if (object && function) 564 cb(ses, scope, object, function); 565 } 566 567 return 0; 568 } 569 570 static bool 571 rpc_session_acl_allowed(struct rpc_session *ses, const char *scope, 572 const char *obj, const char *fun) 573 { 574 struct rpc_session_acl *acl; 575 struct rpc_session_acl_scope *acl_scope; 576 577 acl_scope = avl_find_element(&ses->acls, scope, acl_scope, avl); 578 579 if (acl_scope) { 580 uh_foreach_matching_acl(acl, &acl_scope->acls, obj, fun) 581 return true; 582 } 583 584 return false; 585 } 586 587 static int 588 rpc_handle_access(struct ubus_context *ctx, struct ubus_object *obj, 589 struct ubus_request_data *req, const char *method, 590 struct blob_attr *msg) 591 { 592 struct rpc_session *ses; 593 struct blob_attr *tb[__RPC_SP_MAX]; 594 const char *scope = "ubus"; 595 bool allow; 596 597 blobmsg_parse(perm_policy, __RPC_SP_MAX, tb, blob_data(msg), blob_len(msg)); 598 599 if (!tb[RPC_SP_SID]) 600 return UBUS_STATUS_INVALID_ARGUMENT; 601 602 ses = rpc_session_get(blobmsg_data(tb[RPC_SP_SID])); 603 if (!ses) 604 return UBUS_STATUS_NOT_FOUND; 605 606 blob_buf_init(&buf, 0); 607 608 if (tb[RPC_SP_OBJECT] && tb[RPC_SP_FUNCTION]) 609 { 610 if (tb[RPC_SP_SCOPE]) 611 scope = blobmsg_data(tb[RPC_SP_SCOPE]); 612 613 allow = rpc_session_acl_allowed(ses, scope, 614 blobmsg_data(tb[RPC_SP_OBJECT]), 615 blobmsg_data(tb[RPC_SP_FUNCTION])); 616 617 blobmsg_add_u8(&buf, "access", allow); 618 } 619 else 620 { 621 rpc_session_dump_acls(ses, &buf); 622 } 623 624 ubus_send_reply(ctx, req, buf.head); 625 626 return 0; 627 } 628 629 static void 630 rpc_session_set(struct rpc_session *ses, struct blob_attr *val) 631 { 632 struct rpc_session_data *data; 633 634 data = avl_find_element(&ses->data, blobmsg_name(val), data, avl); 635 if (data) { 636 avl_delete(&ses->data, &data->avl); 637 free(data); 638 } 639 640 data = calloc(1, sizeof(*data) + blob_pad_len(val)); 641 if (!data) 642 return; 643 644 memcpy(data->attr, val, blob_pad_len(val)); 645 data->avl.key = blobmsg_name(data->attr); 646 avl_insert(&ses->data, &data->avl); 647 } 648 649 static int 650 rpc_handle_set(struct ubus_context *ctx, struct ubus_object *obj, 651 struct ubus_request_data *req, const char *method, 652 struct blob_attr *msg) 653 { 654 struct rpc_session *ses; 655 struct blob_attr *tb[__RPC_SS_MAX]; 656 struct blob_attr *attr; 657 int rem; 658 659 blobmsg_parse(set_policy, __RPC_SS_MAX, tb, blob_data(msg), blob_len(msg)); 660 661 if (!tb[RPC_SS_SID] || !tb[RPC_SS_VALUES]) 662 return UBUS_STATUS_INVALID_ARGUMENT; 663 664 ses = rpc_session_get(blobmsg_data(tb[RPC_SS_SID])); 665 if (!ses) 666 return UBUS_STATUS_NOT_FOUND; 667 668 blobmsg_for_each_attr(attr, tb[RPC_SS_VALUES], rem) { 669 if (!blobmsg_name(attr)[0]) 670 continue; 671 672 rpc_session_set(ses, attr); 673 } 674 675 return 0; 676 } 677 678 static int 679 rpc_handle_get(struct ubus_context *ctx, struct ubus_object *obj, 680 struct ubus_request_data *req, const char *method, 681 struct blob_attr *msg) 682 { 683 struct rpc_session *ses; 684 struct rpc_session_data *data; 685 struct blob_attr *tb[__RPC_SG_MAX]; 686 struct blob_attr *attr; 687 void *c; 688 int rem; 689 690 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg)); 691 692 if (!tb[RPC_SG_SID]) 693 return UBUS_STATUS_INVALID_ARGUMENT; 694 695 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID])); 696 if (!ses) 697 return UBUS_STATUS_NOT_FOUND; 698 699 blob_buf_init(&buf, 0); 700 c = blobmsg_open_table(&buf, "values"); 701 702 if (tb[RPC_SG_KEYS]) 703 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) { 704 if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING) 705 continue; 706 707 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl); 708 if (!data) 709 continue; 710 711 blobmsg_add_field(&buf, blobmsg_type(data->attr), 712 blobmsg_name(data->attr), 713 blobmsg_data(data->attr), 714 blobmsg_data_len(data->attr)); 715 } 716 else 717 rpc_session_dump_data(ses, &buf); 718 719 blobmsg_close_table(&buf, c); 720 ubus_send_reply(ctx, req, buf.head); 721 722 return 0; 723 } 724 725 static int 726 rpc_handle_unset(struct ubus_context *ctx, struct ubus_object *obj, 727 struct ubus_request_data *req, const char *method, 728 struct blob_attr *msg) 729 { 730 struct rpc_session *ses; 731 struct rpc_session_data *data, *ndata; 732 struct blob_attr *tb[__RPC_SA_MAX]; 733 struct blob_attr *attr; 734 int rem; 735 736 blobmsg_parse(get_policy, __RPC_SG_MAX, tb, blob_data(msg), blob_len(msg)); 737 738 if (!tb[RPC_SG_SID]) 739 return UBUS_STATUS_INVALID_ARGUMENT; 740 741 ses = rpc_session_get(blobmsg_data(tb[RPC_SG_SID])); 742 if (!ses) 743 return UBUS_STATUS_NOT_FOUND; 744 745 if (!tb[RPC_SG_KEYS]) { 746 avl_remove_all_elements(&ses->data, data, avl, ndata) 747 free(data); 748 return 0; 749 } 750 751 blobmsg_for_each_attr(attr, tb[RPC_SG_KEYS], rem) { 752 if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING) 753 continue; 754 755 data = avl_find_element(&ses->data, blobmsg_data(attr), data, avl); 756 if (!data) 757 continue; 758 759 avl_delete(&ses->data, &data->avl); 760 free(data); 761 } 762 763 return 0; 764 } 765 766 static int 767 rpc_handle_destroy(struct ubus_context *ctx, struct ubus_object *obj, 768 struct ubus_request_data *req, const char *method, 769 struct blob_attr *msg) 770 { 771 struct rpc_session *ses; 772 struct blob_attr *tb; 773 774 blobmsg_parse(sid_policy, __RPC_SI_MAX, &tb, blob_data(msg), blob_len(msg)); 775 776 if (!tb) 777 return UBUS_STATUS_INVALID_ARGUMENT; 778 779 if (!strcmp(blobmsg_get_string(tb), RPC_DEFAULT_SESSION_ID)) 780 return UBUS_STATUS_PERMISSION_DENIED; 781 782 ses = rpc_session_get(blobmsg_data(tb)); 783 if (!ses) 784 return UBUS_STATUS_NOT_FOUND; 785 786 rpc_session_destroy(ses); 787 788 return 0; 789 } 790 791 792 static bool 793 rpc_login_test_password(const char *hash, const char *password) 794 { 795 char *crypt_hash; 796 797 /* password is not set */ 798 if (!hash || !*hash) 799 { 800 return true; 801 } 802 803 /* password hash refers to shadow/passwd */ 804 else if (!strncmp(hash, "$p$", 3)) 805 { 806 #ifdef HAVE_SHADOW 807 struct spwd *sp = getspnam(hash + 3); 808 809 if (!sp) 810 return false; 811 812 return rpc_login_test_password(sp->sp_pwdp, password); 813 #else 814 struct passwd *pw = getpwnam(hash + 3); 815 816 if (!pw) 817 return false; 818 819 return rpc_login_test_password(pw->pw_passwd, password); 820 #endif 821 } 822 823 crypt_hash = crypt(password, hash); 824 825 return (crypt_hash && !strcmp(crypt_hash, hash)); 826 } 827 828 static struct uci_section * 829 rpc_login_test_login(struct uci_context *uci, 830 const char *username, const char *password) 831 { 832 struct uci_package *p = NULL; 833 struct uci_section *s; 834 struct uci_element *e; 835 struct uci_ptr ptr = { .package = "rpcd" }; 836 837 if (!uci_lookup_ptr(uci, &ptr, NULL, false) && ptr.p) { 838 uci_unload(uci, ptr.p); 839 ptr.flags = 0; 840 ptr.p = NULL; 841 } 842 843 uci_load(uci, ptr.package, &p); 844 845 if (!p) 846 return false; 847 848 uci_foreach_element(&p->sections, e) 849 { 850 s = uci_to_section(e); 851 852 if (strcmp(s->type, "login")) 853 continue; 854 855 ptr.section = s->e.name; 856 ptr.s = NULL; 857 858 /* test for matching username */ 859 ptr.option = "username"; 860 ptr.o = NULL; 861 862 if (uci_lookup_ptr(uci, &ptr, NULL, true)) 863 continue; 864 865 if (ptr.o->type != UCI_TYPE_STRING) 866 continue; 867 868 if (strcmp(ptr.o->v.string, username)) 869 continue; 870 871 /* If password is NULL, we're restoring ACLs for an existing session, 872 * in this case do not check the password again. */ 873 if (!password) 874 return ptr.s; 875 876 /* test for matching password */ 877 ptr.option = "password"; 878 ptr.o = NULL; 879 880 if (uci_lookup_ptr(uci, &ptr, NULL, true)) 881 continue; 882 883 if (ptr.o->type != UCI_TYPE_STRING) 884 continue; 885 886 if (rpc_login_test_password(ptr.o->v.string, password)) 887 return ptr.s; 888 } 889 890 return NULL; 891 } 892 893 static bool 894 rpc_login_test_permission(struct uci_section *s, 895 const char *perm, const char *group) 896 { 897 const char *p; 898 struct uci_option *o; 899 struct uci_element *e, *l; 900 901 /* If the login section is not provided, we're setting up acls for the 902 * default session, in this case uncondionally allow access to the 903 * "unauthenticated" access group */ 904 if (!s) { 905 return !strcmp(group, "unauthenticated"); 906 } 907 908 uci_foreach_element(&s->options, e) 909 { 910 o = uci_to_option(e); 911 912 if (o->type != UCI_TYPE_LIST) 913 continue; 914 915 if (strcmp(o->e.name, perm)) 916 continue; 917 918 /* Match negative expressions first. If a negative expression matches 919 * the current group name then deny access. */ 920 uci_foreach_element(&o->v.list, l) { 921 p = l->name; 922 923 if (!p || *p != '!') 924 continue; 925 926 while (isspace(*++p)); 927 928 if (!*p) 929 continue; 930 931 if (!fnmatch(p, group, 0)) 932 return false; 933 } 934 935 uci_foreach_element(&o->v.list, l) { 936 if (!l->name || !*l->name || *l->name == '!') 937 continue; 938 939 if (!fnmatch(l->name, group, 0)) 940 return true; 941 } 942 } 943 944 /* make sure that write permission implies read permission */ 945 if (!strcmp(perm, "read")) 946 return rpc_login_test_permission(s, "write", group); 947 948 return false; 949 } 950 951 static void 952 rpc_login_setup_acl_scope(struct rpc_session *ses, 953 struct blob_attr *acl_perm, 954 struct blob_attr *acl_scope) 955 { 956 struct blob_attr *acl_obj, *acl_func; 957 int rem, rem2; 958 959 /* 960 * Parse ACL scopes in table notation. 961 * 962 * "<scope>": { 963 * "<object>": [ 964 * "<function>", 965 * "<function>", 966 * ... 967 * ] 968 * } 969 */ 970 if (blobmsg_type(acl_scope) == BLOBMSG_TYPE_TABLE) { 971 blobmsg_for_each_attr(acl_obj, acl_scope, rem) { 972 if (blobmsg_type(acl_obj) != BLOBMSG_TYPE_ARRAY) 973 continue; 974 975 blobmsg_for_each_attr(acl_func, acl_obj, rem2) { 976 if (blobmsg_type(acl_func) != BLOBMSG_TYPE_STRING) 977 continue; 978 979 rpc_session_grant(ses, blobmsg_name(acl_scope), 980 blobmsg_name(acl_obj), 981 blobmsg_data(acl_func)); 982 } 983 } 984 } 985 986 /* 987 * Parse ACL scopes in array notation. The permission ("read" or "write") 988 * will be used as function name for each object. 989 * 990 * "<scope>": [ 991 * "<object>", 992 * "<object>", 993 * ... 994 * ] 995 */ 996 else if (blobmsg_type(acl_scope) == BLOBMSG_TYPE_ARRAY) { 997 blobmsg_for_each_attr(acl_obj, acl_scope, rem) { 998 if (blobmsg_type(acl_obj) != BLOBMSG_TYPE_STRING) 999 continue; 1000 1001 rpc_session_grant(ses, blobmsg_name(acl_scope), 1002 blobmsg_data(acl_obj), 1003 blobmsg_name(acl_perm)); 1004 } 1005 } 1006 } 1007 1008 static void 1009 rpc_login_setup_acl_file(struct rpc_session *ses, struct uci_section *login, 1010 const char *path) 1011 { 1012 struct blob_buf acl = { 0 }; 1013 struct blob_attr *acl_group, *acl_perm, *acl_scope; 1014 int rem, rem2, rem3; 1015 1016 blob_buf_init(&acl, 0); 1017 1018 if (!blobmsg_add_json_from_file(&acl, path)) { 1019 fprintf(stderr, "Failed to parse %s\n", path); 1020 goto out; 1021 } 1022 1023 /* Iterate access groups in toplevel object */ 1024 blob_for_each_attr(acl_group, acl.head, rem) { 1025 /* Iterate permission objects in each access group object */ 1026 blobmsg_for_each_attr(acl_perm, acl_group, rem2) { 1027 if (blobmsg_type(acl_perm) != BLOBMSG_TYPE_TABLE) 1028 continue; 1029 1030 /* Only "read" and "write" permissions are defined */ 1031 if (strcmp(blobmsg_name(acl_perm), "read") && 1032 strcmp(blobmsg_name(acl_perm), "write")) 1033 continue; 1034 1035 /* 1036 * Check if the current user context specifies the current 1037 * "read" or "write" permission in the given access group. 1038 */ 1039 if (!rpc_login_test_permission(login, blobmsg_name(acl_perm), 1040 blobmsg_name(acl_group))) 1041 continue; 1042 1043 /* Iterate scope objects within the permission object */ 1044 blobmsg_for_each_attr(acl_scope, acl_perm, rem3) { 1045 /* Setup the scopes of the access group */ 1046 rpc_login_setup_acl_scope(ses, acl_perm, acl_scope); 1047 1048 /* 1049 * Add the access group itself as object to the "access-group" 1050 * meta scope and the the permission level ("read" or "write") 1051 * as function, so 1052 * "<group>": { 1053 * "<permission>": { 1054 * "<scope>": ... 1055 * } 1056 * } 1057 * becomes 1058 * "access-group": { 1059 * "<group>": [ 1060 * "<permission>" 1061 * ] 1062 * } 1063 * 1064 * This allows session clients to easily query the allowed 1065 * access groups without having to test access of each single 1066 * <scope>/<object>/<function> tuple defined in a group. 1067 */ 1068 rpc_session_grant(ses, "access-group", 1069 blobmsg_name(acl_group), 1070 blobmsg_name(acl_perm)); 1071 } 1072 } 1073 } 1074 1075 out: 1076 blob_buf_free(&acl); 1077 } 1078 1079 static void 1080 rpc_login_setup_acls(struct rpc_session *ses, struct uci_section *login) 1081 { 1082 int i; 1083 glob_t gl; 1084 1085 if (glob(RPC_SESSION_ACL_DIR "/*.json", 0, NULL, &gl)) 1086 return; 1087 1088 for (i = 0; i < gl.gl_pathc; i++) 1089 rpc_login_setup_acl_file(ses, login, gl.gl_pathv[i]); 1090 1091 globfree(&gl); 1092 } 1093 1094 static struct rpc_session * 1095 rpc_reclaim_apply_session(const char *expected_username) 1096 { 1097 struct rpc_session_data *username; 1098 struct rpc_session *ses; 1099 1100 if (!apply_sid[0]) 1101 return NULL; 1102 1103 ses = rpc_session_get(apply_sid); 1104 1105 if (!ses) 1106 return NULL; 1107 1108 username = avl_find_element(&ses->data, "username", username, avl); 1109 1110 if (!username || blobmsg_type(username->attr) != BLOBMSG_TYPE_STRING) 1111 return NULL; 1112 1113 if (strcmp(blobmsg_get_string(username->attr), expected_username)) 1114 return NULL; 1115 1116 return ses; 1117 } 1118 1119 static int 1120 rpc_handle_login(struct ubus_context *ctx, struct ubus_object *obj, 1121 struct ubus_request_data *req, const char *method, 1122 struct blob_attr *msg) 1123 { 1124 struct uci_context *uci = NULL; 1125 struct uci_section *login; 1126 struct rpc_session *ses; 1127 struct blob_attr *tb[__RPC_L_MAX]; 1128 int timeout = RPC_DEFAULT_SESSION_TIMEOUT; 1129 int rv = 0; 1130 1131 blobmsg_parse(login_policy, __RPC_L_MAX, tb, blob_data(msg), blob_len(msg)); 1132 1133 if (!tb[RPC_L_USERNAME] || !tb[RPC_L_PASSWORD]) { 1134 rv = UBUS_STATUS_INVALID_ARGUMENT; 1135 goto out; 1136 } 1137 1138 uci = uci_alloc_context(); 1139 1140 if (!uci) { 1141 rv = UBUS_STATUS_UNKNOWN_ERROR; 1142 goto out; 1143 } 1144 1145 login = rpc_login_test_login(uci, blobmsg_get_string(tb[RPC_L_USERNAME]), 1146 blobmsg_get_string(tb[RPC_L_PASSWORD])); 1147 1148 if (!login) { 1149 rv = UBUS_STATUS_PERMISSION_DENIED; 1150 goto out; 1151 } 1152 1153 if (tb[RPC_L_TIMEOUT]) 1154 timeout = blobmsg_get_u32(tb[RPC_L_TIMEOUT]); 1155 1156 /* 1157 * attempt to reclaim a pending apply session, but only accept it 1158 * if the username matches, otherwise perform a new login 1159 */ 1160 1161 ses = rpc_reclaim_apply_session(blobmsg_get_string(tb[RPC_L_USERNAME])); 1162 1163 if (!ses) 1164 ses = rpc_session_create(timeout); 1165 1166 if (!ses) { 1167 rv = UBUS_STATUS_UNKNOWN_ERROR; 1168 goto out; 1169 } 1170 1171 rpc_login_setup_acls(ses, login); 1172 1173 rpc_session_set(ses, tb[RPC_L_USERNAME]); 1174 rpc_session_dump(ses, ctx, req); 1175 1176 out: 1177 if (uci) 1178 uci_free_context(uci); 1179 1180 return rv; 1181 } 1182 1183 1184 static bool 1185 rpc_validate_sid(const char *id) 1186 { 1187 if (!id) 1188 return false; 1189 1190 if (strlen(id) != RPC_SID_LEN) 1191 return false; 1192 1193 while (*id) 1194 if (!isxdigit(*id++)) 1195 return false; 1196 1197 return true; 1198 } 1199 1200 static int 1201 rpc_blob_to_file(const char *path, struct blob_attr *attr) 1202 { 1203 int fd, len; 1204 1205 fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); 1206 1207 if (fd < 0) 1208 return fd; 1209 1210 len = write(fd, attr, blob_pad_len(attr)); 1211 1212 close(fd); 1213 1214 if (len != blob_pad_len(attr)) 1215 { 1216 unlink(path); 1217 return -1; 1218 } 1219 1220 return len; 1221 } 1222 1223 static struct blob_attr * 1224 rpc_blob_from_file(const char *path) 1225 { 1226 int fd = -1, len; 1227 struct stat s; 1228 struct blob_attr head, *attr = NULL; 1229 1230 if (stat(path, &s) || !S_ISREG(s.st_mode)) 1231 return NULL; 1232 1233 fd = open(path, O_RDONLY); 1234 1235 if (fd < 0) 1236 goto fail; 1237 1238 len = read(fd, &head, sizeof(head)); 1239 1240 if (len != sizeof(head) || blob_pad_len(&head) != s.st_size) 1241 goto fail; 1242 1243 attr = calloc(1, s.st_size); 1244 1245 if (!attr) 1246 goto fail; 1247 1248 memcpy(attr, &head, sizeof(head)); 1249 1250 len += read(fd, (char *)attr + sizeof(head), s.st_size - sizeof(head)); 1251 1252 if (len != blob_pad_len(&head)) 1253 goto fail; 1254 1255 close(fd); 1256 1257 return attr; 1258 1259 fail: 1260 if (fd >= 0) 1261 close(fd); 1262 1263 if (attr) 1264 free(attr); 1265 1266 return NULL; 1267 } 1268 1269 static bool 1270 rpc_session_from_blob(struct uci_context *uci, struct blob_attr *attr) 1271 { 1272 int i, rem; 1273 const char *user = NULL; 1274 struct rpc_session *ses; 1275 struct uci_section *login; 1276 struct blob_attr *tb[__RPC_DUMP_MAX], *data; 1277 1278 blobmsg_parse(dump_policy, __RPC_DUMP_MAX, tb, 1279 blob_data(attr), blob_len(attr)); 1280 1281 for (i = 0; i < __RPC_DUMP_MAX; i++) 1282 if (!tb[i]) 1283 return false; 1284 1285 ses = rpc_session_new(); 1286 1287 if (!ses) 1288 return false; 1289 1290 memcpy(ses->id, blobmsg_data(tb[RPC_DUMP_SID]), RPC_SID_LEN); 1291 1292 ses->timeout = blobmsg_get_u32(tb[RPC_DUMP_TIMEOUT]); 1293 1294 blobmsg_for_each_attr(data, tb[RPC_DUMP_DATA], rem) { 1295 rpc_session_set(ses, data); 1296 1297 if (blobmsg_type(data) != BLOBMSG_TYPE_STRING) 1298 continue; 1299 1300 if (!strcmp(blobmsg_name(data), "username")) 1301 user = blobmsg_get_string(data); 1302 } 1303 1304 if (uci && user) { 1305 login = rpc_login_test_login(uci, user, NULL); 1306 if (login) 1307 rpc_login_setup_acls(ses, login); 1308 } 1309 1310 avl_insert(&sessions, &ses->avl); 1311 1312 uloop_timeout_set(&ses->t, blobmsg_get_u64(tb[RPC_DUMP_EXPIRES]) * 1000); 1313 1314 return true; 1315 } 1316 1317 int rpc_session_api_init(struct ubus_context *ctx) 1318 { 1319 struct rpc_session *ses; 1320 1321 static const struct ubus_method session_methods[] = { 1322 UBUS_METHOD("create", rpc_handle_create, new_policy), 1323 UBUS_METHOD("list", rpc_handle_list, sid_policy), 1324 UBUS_METHOD("grant", rpc_handle_acl, acl_policy), 1325 UBUS_METHOD("revoke", rpc_handle_acl, acl_policy), 1326 UBUS_METHOD("access", rpc_handle_access, perm_policy), 1327 UBUS_METHOD("set", rpc_handle_set, set_policy), 1328 UBUS_METHOD("get", rpc_handle_get, get_policy), 1329 UBUS_METHOD("unset", rpc_handle_unset, get_policy), 1330 UBUS_METHOD("destroy", rpc_handle_destroy, sid_policy), 1331 UBUS_METHOD("login", rpc_handle_login, login_policy), 1332 }; 1333 1334 static struct ubus_object_type session_type = 1335 UBUS_OBJECT_TYPE("rpcd-plugin-session", session_methods); 1336 1337 static struct ubus_object obj = { 1338 .name = "session", 1339 .type = &session_type, 1340 .methods = session_methods, 1341 .n_methods = ARRAY_SIZE(session_methods), 1342 }; 1343 1344 avl_init(&sessions, avl_strcmp, false, NULL); 1345 1346 /* setup the default session */ 1347 ses = rpc_session_new(); 1348 1349 if (ses) { 1350 strcpy(ses->id, RPC_DEFAULT_SESSION_ID); 1351 rpc_login_setup_acls(ses, NULL); 1352 avl_insert(&sessions, &ses->avl); 1353 } 1354 1355 return ubus_add_object(ctx, &obj); 1356 } 1357 1358 bool rpc_session_access(const char *sid, const char *scope, 1359 const char *object, const char *function) 1360 { 1361 struct rpc_session *ses = rpc_session_get(sid); 1362 1363 if (!ses) 1364 return false; 1365 1366 return rpc_session_acl_allowed(ses, scope, object, function); 1367 } 1368 1369 void rpc_session_create_cb(struct rpc_session_cb *cb) 1370 { 1371 if (cb && cb->cb) 1372 list_add(&cb->list, &create_callbacks); 1373 } 1374 1375 void rpc_session_destroy_cb(struct rpc_session_cb *cb) 1376 { 1377 if (cb && cb->cb) 1378 list_add(&cb->list, &destroy_callbacks); 1379 } 1380 1381 void rpc_session_freeze(void) 1382 { 1383 struct stat s; 1384 struct rpc_session *ses; 1385 char path[PATH_MAX]; 1386 1387 if (stat(RPC_SESSION_DIRECTORY, &s)) 1388 mkdir(RPC_SESSION_DIRECTORY, 0700); 1389 1390 avl_for_each_element(&sessions, ses, avl) { 1391 /* skip default session */ 1392 if (!strcmp(ses->id, RPC_DEFAULT_SESSION_ID)) 1393 continue; 1394 1395 snprintf(path, sizeof(path) - 1, RPC_SESSION_DIRECTORY "/%s", ses->id); 1396 rpc_session_to_blob(ses, false); 1397 rpc_blob_to_file(path, buf.head); 1398 } 1399 } 1400 1401 void rpc_session_thaw(void) 1402 { 1403 DIR *d; 1404 char path[PATH_MAX]; 1405 struct dirent *e; 1406 struct blob_attr *attr; 1407 struct uci_context *uci; 1408 1409 d = opendir(RPC_SESSION_DIRECTORY); 1410 1411 if (!d) 1412 return; 1413 1414 uci = uci_alloc_context(); 1415 1416 if (!uci) 1417 return; 1418 1419 while ((e = readdir(d)) != NULL) { 1420 if (!rpc_validate_sid(e->d_name)) 1421 continue; 1422 1423 snprintf(path, sizeof(path) - 1, 1424 RPC_SESSION_DIRECTORY "/%s", e->d_name); 1425 1426 attr = rpc_blob_from_file(path); 1427 1428 if (attr) { 1429 rpc_session_from_blob(uci, attr); 1430 free(attr); 1431 } 1432 1433 unlink(path); 1434 } 1435 1436 closedir(d); 1437 1438 uci_free_context(uci); 1439 } 1440
This page was automatically generated by LXR 0.3.1. • OpenWrt