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