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