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