1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright (c) 2012, Joyent, Inc. All rights reserved. 24 */ 25 26 27 #include <sys/task.h> 28 #include <sys/types.h> 29 #include <unistd.h> 30 31 #include <ctype.h> 32 #include <project.h> 33 #include <rctl.h> 34 #include <secdb.h> 35 #include <signal.h> 36 #include <stdlib.h> 37 #include <string.h> 38 #include <strings.h> 39 #include <nss_dbdefs.h> 40 #include <pwd.h> 41 #include <pool.h> 42 #include <libproc.h> 43 #include <priv.h> 44 #include <priv_utils.h> 45 #include <zone.h> 46 #include <sys/pool.h> 47 #include <sys/pool_impl.h> 48 #include <sys/rctl_impl.h> 49 50 static void 51 xstrtolower(char *s) 52 { 53 for (; *s != '\0'; s++) 54 *s = tolower(*s); 55 } 56 57 static void 58 remove_spaces(char *s) 59 { 60 char *current; 61 char *next; 62 63 current = next = s; 64 65 while (*next != '\0') { 66 while (isspace(*next)) 67 next++; 68 *current++ = *next++; 69 } 70 *current = '\0'; 71 } 72 73 int 74 build_rctlblk(rctlblk_t *blk, int comp_num, char *component) 75 { 76 char *signam; 77 int sig = 0; 78 uint_t act = rctlblk_get_local_action(blk, &sig); 79 80 if (comp_num == 0) { 81 /* 82 * Setting privilege level for resource control block. 83 */ 84 xstrtolower(component); 85 86 if (strcmp("basic", component) == 0) { 87 rctlblk_set_privilege(blk, RCPRIV_BASIC); 88 return (0); 89 } 90 91 if (strcmp("priv", component) == 0 || 92 strcmp("privileged", component) == 0) { 93 rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED); 94 return (0); 95 } 96 97 return (-1); 98 } 99 100 if (comp_num == 1) { 101 102 /* 103 * Setting value for resource control block. 104 */ 105 unsigned long long val; 106 char *t; 107 108 /* Negative numbers are not allowed */ 109 if (strchr(component, '-') != NULL) 110 return (-1); 111 112 errno = 0; 113 val = strtoull(component, &t, 10); 114 if (errno != 0 || t == component || *t != '\0') 115 return (-1); 116 117 rctlblk_set_value(blk, (rctl_qty_t)val); 118 return (0); 119 } 120 121 /* 122 * Setting one or more actions on this resource control block. 123 */ 124 if (comp_num >= 2) { 125 if (strcmp("none", component) == 0) { 126 rctlblk_set_local_action(blk, 0, 0); 127 return (0); 128 } 129 130 if (strcmp("deny", component) == 0) { 131 act |= RCTL_LOCAL_DENY; 132 133 rctlblk_set_local_action(blk, act, sig); 134 135 return (0); 136 } 137 138 /* 139 * The last, and trickiest, form of action is the signal 140 * specification. 141 */ 142 if ((signam = strchr(component, '=')) == NULL) 143 return (-1); 144 145 *signam++ = '\0'; 146 147 if (strcmp("sig", component) == 0 || 148 strcmp("signal", component) == 0) { 149 if (strncmp("SIG", signam, 3) == 0) 150 signam += 3; 151 152 if (str2sig(signam, &sig) == -1) 153 return (-1); 154 155 act |= RCTL_LOCAL_SIGNAL; 156 157 rctlblk_set_local_action(blk, act, sig); 158 159 return (0); 160 } 161 } 162 return (-1); 163 } 164 165 /* 166 * States: 167 */ 168 #define INPAREN 0x1 169 170 /* 171 * Errors: 172 */ 173 #define SETFAILED (-1) 174 #define COMPLETE 1 175 #define NESTING 2 176 #define UNCLOSED 3 177 #define CLOSEBEFOREOPEN 4 178 #define BADSPEC 5 179 180 static void 181 reinit_blk(rctlblk_t *blk, int local_action) 182 { 183 rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED); 184 rctlblk_set_value(blk, 0); 185 rctlblk_set_local_flags(blk, 0); 186 rctlblk_set_local_action(blk, local_action, 0); 187 } 188 189 static int 190 rctl_set(char *ctl_name, char *val, struct ps_prochandle *Pr, int flags) 191 { 192 int error = 0; 193 uint_t component = 0; 194 int valuecount = 0; 195 uint_t state = 0; 196 char *component_head; 197 rctlblk_t *blk; 198 rctlblk_t *ablk; 199 int project_entity = 0; 200 int count = 0; 201 char *tmp; 202 int local_act; 203 rctlblk_t *rnext; 204 int teardown_basic = 0; 205 int teardown_priv = 0; 206 207 /* We cannot modify a zone resource control */ 208 if (strncmp(ctl_name, "zone.", strlen("zone.")) == 0) { 209 return (SETFAILED); 210 } 211 212 remove_spaces(val); 213 214 if (strncmp(ctl_name, "project.", strlen("project.")) == 0) { 215 project_entity = 1; 216 } else if ((strncmp(ctl_name, "process.", strlen("process.")) != 0) && 217 (strncmp(ctl_name, "task.", strlen("task.")) != 0)) { 218 return (SETFAILED); 219 } 220 221 /* Determine how many attributes we'll be setting */ 222 for (tmp = val; *tmp != '\0'; tmp++) { 223 if (*tmp == '(') 224 count++; 225 } 226 /* Allocate sufficient memory for rctl blocks */ 227 if ((count == 0) || ((ablk = 228 (rctlblk_t *)malloc(rctlblk_size() * count)) == NULL)) { 229 return (SETFAILED); 230 } 231 blk = ablk; 232 233 /* 234 * In order to set the new rctl's local_action, we'll need the 235 * current value of global_flags. We obtain global_flags by 236 * performing a pr_getrctl(). 237 * 238 * The ctl_name has been verified as valid, so we have no reason 239 * to suspect that pr_getrctl() will return an error. 240 */ 241 (void) pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST); 242 243 244 /* 245 * Set initial local action based on global deny properties. 246 */ 247 rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED); 248 rctlblk_set_value(blk, 0); 249 rctlblk_set_local_flags(blk, 0); 250 251 if (rctlblk_get_global_flags(blk) & RCTL_GLOBAL_DENY_ALWAYS) 252 local_act = RCTL_LOCAL_DENY; 253 else 254 local_act = RCTL_LOCAL_NOACTION; 255 256 rctlblk_set_local_action(blk, local_act, 0); 257 258 for (; ; val++) { 259 260 switch (*val) { 261 case '(': 262 if (state & INPAREN) { 263 error = NESTING; 264 break; 265 } 266 267 state |= INPAREN; 268 component_head = (char *)val + 1; 269 270 break; 271 case ')': 272 if (state & INPAREN) { 273 *val = '\0'; 274 if (component < 2) { 275 error = BADSPEC; 276 break; 277 } 278 if (build_rctlblk(blk, component, 279 component_head) == -1) { 280 error = BADSPEC; 281 break; 282 } 283 state &= ~INPAREN; 284 component = 0; 285 valuecount++; 286 287 if (project_entity && 288 (rctlblk_get_privilege(blk) == 289 RCPRIV_BASIC)) { 290 error = SETFAILED; 291 } else { 292 if (rctlblk_get_privilege(blk) 293 == RCPRIV_BASIC) 294 teardown_basic = 1; 295 296 if (rctlblk_get_privilege(blk) 297 == RCPRIV_PRIVILEGED) 298 teardown_priv = 1; 299 300 if (valuecount > count) { 301 free(ablk); 302 return (SETFAILED); 303 } 304 305 if (valuecount != count) { 306 blk = RCTLBLK_INC(ablk, 307 valuecount); 308 /* re-initialize blk */ 309 reinit_blk(blk, 310 local_act); 311 } 312 } 313 314 } else { 315 error = CLOSEBEFOREOPEN; 316 } 317 break; 318 case ',': 319 if (state & INPAREN) { 320 *val = '\0'; 321 if (build_rctlblk(blk, component, 322 component_head) == -1) 323 error = BADSPEC; 324 325 component++; 326 component_head = (char *)val + 1; 327 328 } 329 break; 330 case '\0': 331 if (valuecount == 0) 332 error = BADSPEC; 333 else if (state & INPAREN) 334 error = UNCLOSED; 335 else 336 error = COMPLETE; 337 break; 338 default: 339 if (!(state & INPAREN)) 340 error = BADSPEC; 341 break; 342 } 343 344 if (error) 345 break; 346 } 347 /* ablk points to array of rctlblk_t */ 348 349 if (valuecount == 0) 350 error = BADSPEC; 351 352 if (error != COMPLETE) { 353 free(ablk); 354 return (error); 355 } 356 357 /* teardown rctls if required */ 358 if (!project_entity) { 359 360 if ((rnext = (rctlblk_t *)malloc(rctlblk_size())) == NULL) { 361 free(ablk); 362 return (SETFAILED); 363 } 364 365 restart: 366 if (pr_getrctl(Pr, ctl_name, NULL, rnext, RCTL_FIRST) == 0) { 367 while (1) { 368 if ((rctlblk_get_privilege(rnext) == 369 RCPRIV_PRIVILEGED) && 370 (teardown_priv == 1)) { 371 (void) pr_setrctl(Pr, ctl_name, NULL, 372 rnext, RCTL_DELETE); 373 goto restart; 374 } 375 if ((rctlblk_get_privilege(rnext) == 376 RCPRIV_BASIC) && (teardown_basic == 1)) { 377 (void) pr_setrctl(Pr, ctl_name, NULL, 378 rnext, RCTL_DELETE); 379 goto restart; 380 } 381 382 if (pr_getrctl(Pr, ctl_name, rnext, rnext, 383 RCTL_NEXT) == -1) 384 break; 385 } 386 } 387 388 free(rnext); 389 } 390 391 /* set rctls */ 392 393 blk = ablk; 394 395 if (project_entity) { 396 if (pr_setprojrctl(Pr, ctl_name, blk, count, flags) == -1) 397 error = SETFAILED; 398 } else { 399 valuecount = 0; 400 while (valuecount < count) { 401 if (pr_setrctl(Pr, ctl_name, 402 NULL, blk, RCTL_INSERT) == -1) { 403 error = SETFAILED; 404 break; 405 } 406 valuecount++; 407 blk = RCTLBLK_INC(ablk, valuecount); 408 } 409 } 410 411 412 413 free(ablk); 414 415 if (error != COMPLETE) 416 return (error); 417 418 return (0); 419 } 420 421 static int 422 rctlwalkfunc(const char *name, void *data) 423 { 424 425 if (strcmp(name, (char *)data) == 0) 426 return (-1); 427 else 428 return (0); 429 430 } 431 432 /* 433 * This routine determines if /dev/pool device is present on the system and 434 * pools are currently enabled. We want to do this directly from libproject 435 * without using libpool's pool_get_status() routine because pools could be 436 * completely removed from the system. Return 1 if pools are enabled, or 437 * 0 otherwise. When used inside local zones, always pretend that pools 438 * are disabled because binding is not allowed and we're already in the 439 * right pool. 440 */ 441 static int 442 pools_enabled(void) 443 { 444 pool_status_t status; 445 int fd; 446 447 if (getzoneid() != GLOBAL_ZONEID) 448 return (0); 449 if ((fd = open("/dev/pool", O_RDONLY)) < 0) 450 return (0); 451 if (ioctl(fd, POOL_STATUSQ, &status) < 0) { 452 (void) close(fd); 453 return (0); 454 } 455 (void) close(fd); 456 return (status.ps_io_state); 457 } 458 459 /* 460 * A pool_name of NULL means to attempt to bind to the default pool. 461 * If the "force" flag is non-zero, the value of "system.bind-default" will be 462 * ignored, and the process will be bound to the default pool if one exists. 463 */ 464 static int 465 bind_to_pool(const char *pool_name, pid_t pid, int force) 466 { 467 pool_value_t *pvals[] = { NULL, NULL }; 468 pool_t **pools; 469 uint_t nelem; 470 uchar_t bval; 471 pool_conf_t *conf; 472 const char *nm; 473 int retval; 474 475 if ((conf = pool_conf_alloc()) == NULL) 476 return (-1); 477 if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) < 0) { 478 /* 479 * Pools configuration file is corrupted; allow logins. 480 */ 481 pool_conf_free(conf); 482 return (0); 483 } 484 if (pool_name != NULL && pool_get_pool(conf, pool_name) != NULL) { 485 /* 486 * There was a project.pool entry, and the pool it refers to 487 * is a valid (active) pool. 488 */ 489 (void) pool_conf_close(conf); 490 pool_conf_free(conf); 491 if (pool_set_binding(pool_name, P_PID, pid) != PO_SUCCESS) { 492 if (pool_error() != POE_SYSTEM) 493 errno = EINVAL; 494 return (-1); 495 } 496 return (0); 497 } 498 499 /* 500 * Bind to the pool with 'pool.default' = 'true' if 501 * 'system.bind-default' = 'true'. 502 */ 503 if ((pvals[0] = pool_value_alloc()) == NULL) { 504 pool_conf_close(conf); 505 pool_conf_free(conf); 506 return (-1); 507 } 508 if (!force && pool_get_property(conf, pool_conf_to_elem(conf), 509 "system.bind-default", pvals[0]) != POC_BOOL || 510 pool_value_get_bool(pvals[0], &bval) != PO_SUCCESS || 511 bval == PO_FALSE) { 512 pool_value_free(pvals[0]); 513 pool_conf_close(conf); 514 pool_conf_free(conf); 515 errno = pool_name == NULL ? EACCES : ESRCH; 516 return (-1); 517 } 518 (void) pool_value_set_name(pvals[0], "pool.default"); 519 pool_value_set_bool(pvals[0], PO_TRUE); 520 if ((pools = pool_query_pools(conf, &nelem, pvals)) == NULL) { 521 /* 522 * No default pools exist. 523 */ 524 pool_value_free(pvals[0]); 525 pool_conf_close(conf); 526 pool_conf_free(conf); 527 errno = pool_name == NULL ? EACCES : ESRCH; 528 return (-1); 529 } 530 if (nelem != 1 || 531 pool_get_property(conf, pool_to_elem(conf, pools[0]), "pool.name", 532 pvals[0]) != POC_STRING) { 533 /* 534 * Configuration is invalid. 535 */ 536 free(pools); 537 pool_value_free(pvals[0]); 538 (void) pool_conf_close(conf); 539 pool_conf_free(conf); 540 return (0); 541 } 542 free(pools); 543 (void) pool_conf_close(conf); 544 pool_conf_free(conf); 545 (void) pool_value_get_string(pvals[0], &nm); 546 if (pool_set_binding(nm, P_PID, pid) != PO_SUCCESS) { 547 if (pool_error() != POE_SYSTEM) 548 errno = EINVAL; 549 retval = -1; 550 } else { 551 retval = 0; 552 } 553 pool_value_free(pvals[0]); 554 return (retval); 555 } 556 557 /* 558 * Changes the assigned project, task and resource pool of a stopped target 559 * process. 560 * 561 * We may not have access to the project table if our target process is in 562 * getprojbyname()'s execution path. Similarly, we may not be able to get user 563 * information if the target process is in getpwnam()'s execution path. Thus we 564 * give the caller the option of skipping these checks by providing a pointer to 565 * a pre-validated project structure in proj (whose name matches project_name) 566 * and taking responsibility for ensuring that the target process' owner is a 567 * member of the target project. 568 * 569 * Callers of this function should always provide a pre-validated project 570 * structure in proj unless they can be sure that the target process will never 571 * be in setproject_proc()'s execution path. 572 */ 573 574 projid_t 575 setproject_proc(const char *project_name, const char *user_name, int flags, 576 pid_t pid, struct ps_prochandle *Pr, struct project *proj) 577 { 578 char pwdbuf[NSS_BUFLEN_PASSWD]; 579 char prbuf[PROJECT_BUFSZ]; 580 projid_t projid; 581 struct passwd pwd; 582 int i; 583 int unknown = 0; 584 int ret = 0; 585 kva_t *kv_array; 586 struct project local_proj; /* space to store proj if not provided */ 587 const char *pool_name = NULL; 588 589 if (project_name != NULL) { 590 /* 591 * Sanity checks. 592 */ 593 if (strcmp(project_name, "") == 0 || 594 user_name == NULL) { 595 errno = EINVAL; 596 return (SETPROJ_ERR_TASK); 597 } 598 599 /* 600 * If proj is NULL, acquire project information to ensure that 601 * project_name is a valid project, and confirm that user_name 602 * exists and is a member of the specified project. 603 */ 604 if (proj == NULL) { 605 if ((proj = getprojbyname(project_name, &local_proj, 606 prbuf, PROJECT_BUFSZ)) == NULL) { 607 errno = ESRCH; 608 return (SETPROJ_ERR_TASK); 609 } 610 611 if (getpwnam_r(user_name, &pwd, 612 pwdbuf, NSS_BUFLEN_PASSWD) == NULL) { 613 errno = ESRCH; 614 return (SETPROJ_ERR_TASK); 615 } 616 /* 617 * Root can join any project. 618 */ 619 if (pwd.pw_uid != (uid_t)0 && 620 !inproj(user_name, project_name, prbuf, 621 PROJECT_BUFSZ)) { 622 errno = ESRCH; 623 return (SETPROJ_ERR_TASK); 624 } 625 } 626 projid = proj->pj_projid; 627 } else { 628 projid = getprojid(); 629 } 630 631 632 if ((kv_array = _str2kva(proj->pj_attr, KV_ASSIGN, 633 KV_DELIMITER)) != NULL) { 634 for (i = 0; i < kv_array->length; i++) { 635 if (strcmp(kv_array->data[i].key, 636 "project.pool") == 0) { 637 pool_name = kv_array->data[i].value; 638 } 639 if (strcmp(kv_array->data[i].key, "task.final") == 0) { 640 flags |= TASK_FINAL; 641 } 642 } 643 } 644 645 /* 646 * Bind process to a pool only if pools are configured 647 */ 648 if (pools_enabled() == 1) { 649 char *old_pool_name; 650 /* 651 * Attempt to bind to pool before calling 652 * settaskid(). 653 */ 654 old_pool_name = pool_get_binding(pid); 655 if (bind_to_pool(pool_name, pid, 0) != 0) { 656 if (old_pool_name) 657 free(old_pool_name); 658 _kva_free(kv_array); 659 return (SETPROJ_ERR_POOL); 660 } 661 if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) { 662 int saved_errno = errno; 663 664 /* 665 * Undo pool binding. 666 */ 667 (void) bind_to_pool(old_pool_name, pid, 1); 668 if (old_pool_name) 669 free(old_pool_name); 670 _kva_free(kv_array); 671 /* 672 * Restore errno 673 */ 674 errno = saved_errno; 675 return (SETPROJ_ERR_TASK); 676 } 677 if (old_pool_name) 678 free(old_pool_name); 679 } else { 680 /* 681 * Pools are not configured, so simply create new task. 682 */ 683 if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) { 684 _kva_free(kv_array); 685 return (SETPROJ_ERR_TASK); 686 } 687 } 688 689 if (project_name == NULL) { 690 /* 691 * In the case that we are starting a new task in the 692 * current project, we are finished, since the current 693 * resource controls will still apply. (Implicit behaviour: 694 * a project must be entirely logged out before name 695 * service changes will take effect.) 696 */ 697 _kva_free(kv_array); 698 return (projid); 699 } 700 701 if (kv_array == NULL) 702 return (0); 703 704 for (i = 0; i < kv_array->length; i++) { 705 /* 706 * Providing a special, i.e. a non-resource control, key? Then 707 * parse that key here and end with "continue;". 708 */ 709 710 /* 711 * For generic bindings, the kernel performs the binding, as 712 * these are resource controls advertised by kernel subsystems. 713 */ 714 715 /* 716 * Check for known attribute name. 717 */ 718 errno = 0; 719 if (rctl_walk(rctlwalkfunc, (void *)kv_array->data[i].key) 720 == 0) 721 continue; 722 if (errno) { 723 _kva_free(kv_array); 724 return (SETPROJ_ERR_TASK); 725 } 726 727 ret = rctl_set(kv_array->data[i].key, 728 kv_array->data[i].value, Pr, flags & TASK_PROJ_MASK); 729 730 if (ret && unknown == 0) { 731 /* 732 * We only report the first failure. 733 */ 734 unknown = i + 1; 735 } 736 737 if (ret && ret != SETFAILED) { 738 /* 739 * We abort if we couldn't set a component, but if 740 * it's merely that the system didn't recognize it, we 741 * continue, as this could be a third party attribute. 742 */ 743 break; 744 } 745 } 746 _kva_free(kv_array); 747 748 return (unknown); 749 } 750 751 projid_t 752 setproject(const char *project_name, const char *user_name, int flags) 753 { 754 return (setproject_proc(project_name, user_name, flags, P_MYID, NULL, 755 NULL)); 756 } 757 758 759 priv_set_t * 760 setproject_initpriv(void) 761 { 762 static priv_t taskpriv = PRIV_PROC_TASKID; 763 static priv_t rctlpriv = PRIV_SYS_RESOURCE; 764 static priv_t poolpriv = PRIV_SYS_RES_CONFIG; 765 static priv_t schedpriv = PRIV_PROC_PRIOCNTL; 766 int res; 767 768 priv_set_t *nset; 769 770 if (getzoneid() == GLOBAL_ZONEID) { 771 res = __init_suid_priv(0, taskpriv, rctlpriv, poolpriv, 772 schedpriv, (char *)NULL); 773 } else { 774 res = __init_suid_priv(0, taskpriv, rctlpriv, (char *)NULL); 775 } 776 777 if (res != 0) 778 return (NULL); 779 780 nset = priv_allocset(); 781 if (nset != NULL) { 782 priv_emptyset(nset); 783 (void) priv_addset(nset, taskpriv); 784 (void) priv_addset(nset, rctlpriv); 785 /* 786 * Only need these if we need to change pools, which can 787 * only happen if the target is in the global zone. Rather 788 * than checking the target's zone just check our own 789 * (since if we're in a non-global zone we won't be able 790 * to control processes in other zones). 791 */ 792 if (getzoneid() == GLOBAL_ZONEID) { 793 (void) priv_addset(nset, poolpriv); 794 (void) priv_addset(nset, schedpriv); 795 } 796 } 797 return (nset); 798 } 799