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) 866 continue; 867 868 if (ptr.o->type != UCI_TYPE_STRING) 869 continue; 870 871 if (strcmp(ptr.o->v.string, username)) 872 continue; 873 874 /* If password is NULL, we're restoring ACLs for an existing session, 875 * in this case do not check the password again. */ 876 if (!password) 877 return ptr.s; 878 879 /* test for matching password */ 880 ptr.option = "password"; 881 ptr.o = NULL; 882 883 if (uci_lookup_ptr(uci, &ptr, NULL, true)) 884 continue; 885 886 if (!ptr.o) 887 continue; 888 889 if (ptr.o->type != UCI_TYPE_STRING) 890 continue; 891 892 if (rpc_login_test_password(ptr.o->v.string, password)) 893 return ptr.s; 894 } 895 896 return NULL; 897 } 898 899 static bool 900 rpc_login_test_permission(struct uci_section *s, 901 const char *perm, const char *group) 902 { 903 const char *p; 904 struct uci_option *o; 905 struct uci_element *e, *l; 906 907 /* If the login section is not provided, we're setting up acls for the 908 * default session, in this case uncondionally allow access to the 909 * "unauthenticated" access group */ 910 if (!s) { 911 return !strcmp(group, "unauthenticated"); 912 } 913 914 uci_foreach_element(&s->options, e) 915 { 916 o = uci_to_option(e); 917 918 if (o->type != UCI_TYPE_LIST) 919 continue; 920 921 if (strcmp(o->e.name, perm)) 922 continue; 923 924 /* Match negative expressions first. If a negative expression matches 925 * the current group name then deny access. */ 926 uci_foreach_element(&o->v.list, l) { 927 p = l->name; 928 929 if (!p || *p != '!') 930 continue; 931 932 while (isspace(*++p)); 933 934 if (!*p) 935 continue; 936 937 if (!fnmatch(p, group, 0)) 938 return false; 939 } 940 941 uci_foreach_element(&o->v.list, l) { 942 if (!l->name || !*l->name || *l->name == '!') 943 continue; 944 945 if (!fnmatch(l->name, group, 0)) 946 return true; 947 } 948 } 949 950 /* make sure that write permission implies read permission */ 951 if (!strcmp(perm, "read")) 952 return rpc_login_test_permission(s, "write", group); 953 954 return false; 955 } 956 957 static void 958 rpc_login_setup_acl_scope(struct rpc_session *ses, 959 struct blob_attr *acl_perm, 960 struct blob_attr *acl_scope) 961 { 962 struct blob_attr *acl_obj, *acl_func; 963 int rem, rem2; 964 965 /* 966 * Parse ACL scopes in table notation. 967 * 968 * "<scope>": { 969 * "<object>": [ 970 * "<function>", 971 * "<function>", 972 * ... 973 * ] 974 * } 975 */ 976 if (blobmsg_type(acl_scope) == BLOBMSG_TYPE_TABLE) { 977 blobmsg_for_each_attr(acl_obj, acl_scope, rem) { 978 if (blobmsg_type(acl_obj) != BLOBMSG_TYPE_ARRAY) 979 continue; 980 981 blobmsg_for_each_attr(acl_func, acl_obj, rem2) { 982 if (blobmsg_type(acl_func) != BLOBMSG_TYPE_STRING) 983 continue; 984 985 rpc_session_grant(ses, blobmsg_name(acl_scope), 986 blobmsg_name(acl_obj), 987 blobmsg_data(acl_func)); 988 } 989 } 990 } 991 992 /* 993 * Parse ACL scopes in array notation. The permission ("read" or "write") 994 * will be used as function name for each object. 995 * 996 * "<scope>": [ 997 * "<object>", 998 * "<object>", 999 * ... 1000 * ] 1001 */ 1002 else if (blobmsg_type(acl_scope) == BLOBMSG_TYPE_ARRAY) { 1003 blobmsg_for_each_attr(acl_obj, acl_scope, rem) { 1004 if (blobmsg_type(acl_obj) != BLOBMSG_TYPE_STRING) 1005 continue; 1006 1007 rpc_session_grant(ses, blobmsg_name(acl_scope), 1008 blobmsg_data(acl_obj), 1009 blobmsg_name(acl_perm)); 1010 } 1011 } 1012 } 1013 1014 static void 1015 rpc_login_setup_acl_file(struct rpc_session *ses, struct uci_section *login, 1016 const char *path) 1017 { 1018 struct blob_buf acl = { 0 }; 1019 struct blob_attr *acl_group, *acl_perm, *acl_scope; 1020 int rem, rem2, rem3; 1021 1022 blob_buf_init(&acl, 0); 1023 1024 if (!blobmsg_add_json_from_file(&acl, path)) { 1025 fprintf(stderr, "Failed to parse %s\n", path); 1026 goto out; 1027 } 1028 1029 /* Iterate access groups in toplevel object */ 1030 blob_for_each_attr(acl_group, acl.head, rem) { 1031 /* Iterate permission objects in each access group object */ 1032 blobmsg_for_each_attr(acl_perm, acl_group, rem2) { 1033 if (blobmsg_type(acl_perm) != BLOBMSG_TYPE_TABLE) 1034 continue; 1035 1036 /* Only "read" and "write" permissions are defined */ 1037 if (strcmp(blobmsg_name(acl_perm), "read") && 1038 strcmp(blobmsg_name(acl_perm), "write")) 1039 continue; 1040 1041 /* 1042 * Check if the current user context specifies the current 1043 * "read" or "write" permission in the given access group. 1044 */ 1045 if (!rpc_login_test_permission(login, blobmsg_name(acl_perm), 1046 blobmsg_name(acl_group))) 1047 continue; 1048 1049 /* Iterate scope objects within the permission object */ 1050 blobmsg_for_each_attr(acl_scope, acl_perm, rem3) { 1051 /* Setup the scopes of the access group */ 1052 rpc_login_setup_acl_scope(ses, acl_perm, acl_scope); 1053 1054 /* 1055 * Add the access group itself as object to the "access-group" 1056 * meta scope and the the permission level ("read" or "write") 1057 * as function, so 1058 * "<group>": { 1059 * "<permission>": { 1060 * "<scope>": ... 1061 * } 1062 * } 1063 * becomes 1064 * "access-group": { 1065 * "<group>": [ 1066 * "<permission>" 1067 * ] 1068 * } 1069 * 1070 * This allows session clients to easily query the allowed 1071 * access groups without having to test access of each single 1072 * <scope>/<object>/<function> tuple defined in a group. 1073 */ 1074 rpc_session_grant(ses, "access-group", 1075 blobmsg_name(acl_group), 1076 blobmsg_name(acl_perm)); 1077 } 1078 } 1079 } 1080 1081 out: 1082 blob_buf_free(&acl); 1083 } 1084 1085 static void 1086 rpc_login_setup_acls(struct rpc_session *ses, struct uci_section *login) 1087 { 1088 int i; 1089 glob_t gl; 1090 1091 if (glob(RPC_SESSION_ACL_DIR "/*.json", 0, NULL, &gl)) 1092 return; 1093 1094 for (i = 0; i < gl.gl_pathc; i++) 1095 rpc_login_setup_acl_file(ses, login, gl.gl_pathv[i]); 1096 1097 globfree(&gl); 1098 } 1099 1100 static struct rpc_session * 1101 rpc_reclaim_apply_session(const char *expected_username) 1102 { 1103 struct rpc_session_data *username; 1104 struct rpc_session *ses; 1105 1106 if (!apply_sid[0]) 1107 return NULL; 1108 1109 ses = rpc_session_get(apply_sid); 1110 1111 if (!ses) 1112 return NULL; 1113 1114 username = avl_find_element(&ses->data, "username", username, avl); 1115 1116 if (!username || blobmsg_type(username->attr) != BLOBMSG_TYPE_STRING) 1117 return NULL; 1118 1119 if (strcmp(blobmsg_get_string(username->attr), expected_username)) 1120 return NULL; 1121 1122 return ses; 1123 } 1124 1125 static int 1126 rpc_handle_login(struct ubus_context *ctx, struct ubus_object *obj, 1127 struct ubus_request_data *req, const char *method, 1128 struct blob_attr *msg) 1129 { 1130 struct uci_context *uci = NULL; 1131 struct uci_section *login; 1132 struct rpc_session *ses; 1133 struct blob_attr *tb[__RPC_L_MAX]; 1134 int timeout = RPC_DEFAULT_SESSION_TIMEOUT; 1135 int rv = 0; 1136 1137 blobmsg_parse(login_policy, __RPC_L_MAX, tb, blob_data(msg), blob_len(msg)); 1138 1139 if (!tb[RPC_L_USERNAME] || !tb[RPC_L_PASSWORD]) { 1140 rv = UBUS_STATUS_INVALID_ARGUMENT; 1141 goto out; 1142 } 1143 1144 uci = uci_alloc_context(); 1145 1146 if (!uci) { 1147 rv = UBUS_STATUS_UNKNOWN_ERROR; 1148 goto out; 1149 } 1150 1151 login = rpc_login_test_login(uci, blobmsg_get_string(tb[RPC_L_USERNAME]), 1152 blobmsg_get_string(tb[RPC_L_PASSWORD])); 1153 1154 if (!login) { 1155 rv = UBUS_STATUS_PERMISSION_DENIED; 1156 goto out; 1157 } 1158 1159 if (tb[RPC_L_TIMEOUT]) 1160 timeout = blobmsg_get_u32(tb[RPC_L_TIMEOUT]); 1161 1162 /* 1163 * attempt to reclaim a pending apply session, but only accept it 1164 * if the username matches, otherwise perform a new login 1165 */ 1166 1167 ses = rpc_reclaim_apply_session(blobmsg_get_string(tb[RPC_L_USERNAME])); 1168 1169 if (!ses) 1170 ses = rpc_session_create(timeout); 1171 1172 if (!ses) { 1173 rv = UBUS_STATUS_UNKNOWN_ERROR; 1174 goto out; 1175 } 1176 1177 rpc_login_setup_acls(ses, login); 1178 1179 rpc_session_set(ses, tb[RPC_L_USERNAME]); 1180 rpc_session_dump(ses, ctx, req); 1181 1182 out: 1183 if (uci) 1184 uci_free_context(uci); 1185 1186 return rv; 1187 } 1188 1189 1190 static bool 1191 rpc_validate_sid(const char *id) 1192 { 1193 if (!id) 1194 return false; 1195 1196 if (strlen(id) != RPC_SID_LEN) 1197 return false; 1198 1199 while (*id) 1200 if (!isxdigit(*id++)) 1201 return false; 1202 1203 return true; 1204 } 1205 1206 static int 1207 rpc_blob_to_file(const char *path, struct blob_attr *attr) 1208 { 1209 int fd, len; 1210 1211 fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); 1212 1213 if (fd < 0) 1214 return fd; 1215 1216 len = write(fd, attr, blob_pad_len(attr)); 1217 1218 close(fd); 1219 1220 if (len != blob_pad_len(attr)) 1221 { 1222 unlink(path); 1223 return -1; 1224 } 1225 1226 return len; 1227 } 1228 1229 static struct blob_attr * 1230 rpc_blob_from_file(const char *path) 1231 { 1232 int fd = -1, len; 1233 struct stat s; 1234 struct blob_attr head, *attr = NULL; 1235 1236 if (stat(path, &s) || !S_ISREG(s.st_mode)) 1237 return NULL; 1238 1239 fd = open(path, O_RDONLY); 1240 1241 if (fd < 0) 1242 goto fail; 1243 1244 len = read(fd, &head, sizeof(head)); 1245 1246 if (len != sizeof(head) || blob_pad_len(&head) != s.st_size) 1247 goto fail; 1248 1249 attr = calloc(1, s.st_size); 1250 1251 if (!attr) 1252 goto fail; 1253 1254 memcpy(attr, &head, sizeof(head)); 1255 1256 len += read(fd, (char *)attr + sizeof(head), s.st_size - sizeof(head)); 1257 1258 if (len != blob_pad_len(&head)) 1259 goto fail; 1260 1261 close(fd); 1262 1263 return attr; 1264 1265 fail: 1266 if (fd >= 0) 1267 close(fd); 1268 1269 if (attr) 1270 free(attr); 1271 1272 return NULL; 1273 } 1274 1275 static bool 1276 rpc_session_from_blob(struct uci_context *uci, struct blob_attr *attr) 1277 { 1278 int i, rem; 1279 const char *user = NULL; 1280 struct rpc_session *ses; 1281 struct uci_section *login; 1282 struct blob_attr *tb[__RPC_DUMP_MAX], *data; 1283 1284 blobmsg_parse(dump_policy, __RPC_DUMP_MAX, tb, 1285 blob_data(attr), blob_len(attr)); 1286 1287 for (i = 0; i < __RPC_DUMP_MAX; i++) 1288 if (!tb[i]) 1289 return false; 1290 1291 ses = rpc_session_new(); 1292 1293 if (!ses) 1294 return false; 1295 1296 memcpy(ses->id, blobmsg_data(tb[RPC_DUMP_SID]), RPC_SID_LEN); 1297 1298 ses->timeout = blobmsg_get_u32(tb[RPC_DUMP_TIMEOUT]); 1299 1300 blobmsg_for_each_attr(data, tb[RPC_DUMP_DATA], rem) { 1301 rpc_session_set(ses, data); 1302 1303 if (blobmsg_type(data) != BLOBMSG_TYPE_STRING) 1304 continue; 1305 1306 if (!strcmp(blobmsg_name(data), "username")) 1307 user = blobmsg_get_string(data); 1308 } 1309 1310 if (uci && user) { 1311 login = rpc_login_test_login(uci, user, NULL); 1312 if (login) 1313 rpc_login_setup_acls(ses, login); 1314 } 1315 1316 avl_insert(&sessions, &ses->avl); 1317 1318 uloop_timeout_set(&ses->t, blobmsg_get_u64(tb[RPC_DUMP_EXPIRES]) * 1000); 1319 1320 return true; 1321 } 1322 1323 int rpc_session_api_init(struct ubus_context *ctx) 1324 { 1325 struct rpc_session *ses; 1326 1327 static const struct ubus_method session_methods[] = { 1328 UBUS_METHOD("create", rpc_handle_create, new_policy), 1329 UBUS_METHOD("list", rpc_handle_list, sid_policy), 1330 UBUS_METHOD("grant", rpc_handle_acl, acl_policy), 1331 UBUS_METHOD("revoke", rpc_handle_acl, acl_policy), 1332 UBUS_METHOD("access", rpc_handle_access, perm_policy), 1333 UBUS_METHOD("set", rpc_handle_set, set_policy), 1334 UBUS_METHOD("get", rpc_handle_get, get_policy), 1335 UBUS_METHOD("unset", rpc_handle_unset, get_policy), 1336 UBUS_METHOD("destroy", rpc_handle_destroy, sid_policy), 1337 UBUS_METHOD("login", rpc_handle_login, login_policy), 1338 }; 1339 1340 static struct ubus_object_type session_type = 1341 UBUS_OBJECT_TYPE("rpcd-plugin-session", session_methods); 1342 1343 static struct ubus_object obj = { 1344 .name = "session", 1345 .type = &session_type, 1346 .methods = session_methods, 1347 .n_methods = ARRAY_SIZE(session_methods), 1348 }; 1349 1350 avl_init(&sessions, avl_strcmp, false, NULL); 1351 1352 /* setup the default session */ 1353 ses = rpc_session_new(); 1354 1355 if (ses) { 1356 strcpy(ses->id, RPC_DEFAULT_SESSION_ID); 1357 rpc_login_setup_acls(ses, NULL); 1358 avl_insert(&sessions, &ses->avl); 1359 } 1360 1361 return ubus_add_object(ctx, &obj); 1362 } 1363 1364 bool rpc_session_access(const char *sid, const char *scope, 1365 const char *object, const char *function) 1366 { 1367 struct rpc_session *ses = rpc_session_get(sid); 1368 1369 if (!ses) 1370 return false; 1371 1372 return rpc_session_acl_allowed(ses, scope, object, function); 1373 } 1374 1375 void rpc_session_create_cb(struct rpc_session_cb *cb) 1376 { 1377 if (cb && cb->cb) 1378 list_add(&cb->list, &create_callbacks); 1379 } 1380 1381 void rpc_session_destroy_cb(struct rpc_session_cb *cb) 1382 { 1383 if (cb && cb->cb) 1384 list_add(&cb->list, &destroy_callbacks); 1385 } 1386 1387 void rpc_session_freeze(void) 1388 { 1389 struct stat s; 1390 struct rpc_session *ses; 1391 char path[PATH_MAX]; 1392 1393 if (stat(RPC_SESSION_DIRECTORY, &s)) 1394 mkdir(RPC_SESSION_DIRECTORY, 0700); 1395 1396 avl_for_each_element(&sessions, ses, avl) { 1397 /* skip default session */ 1398 if (!strcmp(ses->id, RPC_DEFAULT_SESSION_ID)) 1399 continue; 1400 1401 snprintf(path, sizeof(path) - 1, RPC_SESSION_DIRECTORY "/%s", ses->id); 1402 rpc_session_to_blob(ses, false); 1403 rpc_blob_to_file(path, buf.head); 1404 } 1405 } 1406 1407 void rpc_session_thaw(void) 1408 { 1409 DIR *d; 1410 char path[PATH_MAX]; 1411 struct dirent *e; 1412 struct blob_attr *attr; 1413 struct uci_context *uci; 1414 1415 d = opendir(RPC_SESSION_DIRECTORY); 1416 1417 if (!d) 1418 return; 1419 1420 uci = uci_alloc_context(); 1421 1422 if (!uci) 1423 return; 1424 1425 while ((e = readdir(d)) != NULL) { 1426 if (!rpc_validate_sid(e->d_name)) 1427 continue; 1428 1429 snprintf(path, sizeof(path) - 1, 1430 RPC_SESSION_DIRECTORY "/%s", e->d_name); 1431 1432 attr = rpc_blob_from_file(path); 1433 1434 if (attr) { 1435 rpc_session_from_blob(uci, attr); 1436 free(attr); 1437 } 1438 1439 unlink(path); 1440 } 1441 1442 closedir(d); 1443 1444 uci_free_context(uci); 1445 } 1446
This page was automatically generated by LXR 0.3.1. • OpenWrt