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 2006 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 <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 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 int 180 rctl_set(char *ctl_name, char *val, struct ps_prochandle *Pr) 181 { 182 int error = 0; 183 uint_t component = 0; 184 int valuecount = 0; 185 uint_t state = 0; 186 char *component_head; 187 rctlblk_t *blk; 188 189 remove_spaces(val); 190 if ((blk = malloc(rctlblk_size())) == NULL) { 191 return (SETFAILED); 192 } 193 194 /* 195 * Tear down everything with this ctl name. 196 */ 197 while (pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST) != -1 && 198 rctlblk_get_privilege(blk) != RCPRIV_SYSTEM) { 199 (void) pr_setrctl(Pr, ctl_name, NULL, blk, RCTL_DELETE); 200 } 201 202 /* 203 * Set initial local action based on global deny properties. 204 */ 205 rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED); 206 rctlblk_set_value(blk, 0); 207 rctlblk_set_local_flags(blk, 0); 208 if (rctlblk_get_global_flags(blk) & RCTL_GLOBAL_DENY_ALWAYS) 209 rctlblk_set_local_action(blk, RCTL_LOCAL_DENY, 0); 210 else 211 rctlblk_set_local_action(blk, RCTL_LOCAL_NOACTION, 0); 212 213 214 for (; ; val++) { 215 switch (*val) { 216 case '(': 217 if (state & INPAREN) { 218 error = NESTING; 219 break; 220 } 221 222 state |= INPAREN; 223 component_head = (char *)val + 1; 224 225 break; 226 case ')': 227 if (state & INPAREN) { 228 *val = '\0'; 229 if (component < 2) { 230 error = BADSPEC; 231 break; 232 } 233 if (build_rctlblk(blk, component, 234 component_head) == -1) { 235 error = BADSPEC; 236 break; 237 } 238 state &= ~INPAREN; 239 component = 0; 240 valuecount++; 241 if (pr_setrctl(Pr, ctl_name, NULL, blk, 242 RCTL_INSERT) == -1) 243 error = SETFAILED; 244 245 /* re-initialize block */ 246 rctlblk_set_privilege(blk, 247 RCPRIV_PRIVILEGED); 248 rctlblk_set_value(blk, 0); 249 rctlblk_set_local_flags(blk, 0); 250 if (rctlblk_get_global_flags(blk) & 251 RCTL_GLOBAL_DENY_ALWAYS) 252 rctlblk_set_local_action(blk, 253 RCTL_LOCAL_DENY, 0); 254 else 255 rctlblk_set_local_action(blk, 256 RCTL_LOCAL_NOACTION, 0); 257 } else { 258 error = CLOSEBEFOREOPEN; 259 } 260 break; 261 case ',': 262 if (state & INPAREN) { 263 *val = '\0'; 264 if (build_rctlblk(blk, component, 265 component_head) == -1) 266 error = BADSPEC; 267 268 component++; 269 component_head = (char *)val + 1; 270 271 } 272 break; 273 case '\0': 274 if (valuecount == 0) 275 error = BADSPEC; 276 else if (state & INPAREN) 277 error = UNCLOSED; 278 else 279 error = COMPLETE; 280 break; 281 default: 282 if (!(state & INPAREN)) 283 error = BADSPEC; 284 break; 285 } 286 287 if (error) 288 break; 289 } 290 291 free(blk); 292 293 if (valuecount == 0) 294 error = BADSPEC; 295 296 if (error != COMPLETE) 297 return (error); 298 299 return (0); 300 } 301 302 static int 303 rctlwalkfunc(const char *name, void *data) 304 { 305 306 if (strcmp(name, (char *)data) == 0) 307 return (-1); 308 else 309 return (0); 310 311 } 312 313 /* 314 * This routine determines if /dev/pool device is present on the system and 315 * pools are currently enabled. We want to do this directly from libproject 316 * without using libpool's pool_get_status() routine because pools could be 317 * completely removed from the system. Return 1 if pools are enabled, or 318 * 0 otherwise. When used inside local zones, always pretend that pools 319 * are disabled because binding is not allowed and we're already in the 320 * right pool. 321 */ 322 static int 323 pools_enabled(void) 324 { 325 pool_status_t status; 326 int fd; 327 328 if (getzoneid() != GLOBAL_ZONEID) 329 return (0); 330 if ((fd = open("/dev/pool", O_RDONLY)) < 0) 331 return (0); 332 if (ioctl(fd, POOL_STATUSQ, &status) < 0) { 333 (void) close(fd); 334 return (0); 335 } 336 (void) close(fd); 337 return (status.ps_io_state); 338 } 339 340 /* 341 * A pool_name of NULL means to attempt to bind to the default pool. 342 * If the "force" flag is non-zero, the value of "system.bind-default" will be 343 * ignored, and the process will be bound to the default pool if one exists. 344 */ 345 static int 346 bind_to_pool(const char *pool_name, pid_t pid, int force) 347 { 348 pool_value_t *pvals[] = { NULL, NULL }; 349 pool_t **pools; 350 uint_t nelem; 351 uchar_t bval; 352 pool_conf_t *conf; 353 const char *nm; 354 int retval; 355 356 if ((conf = pool_conf_alloc()) == NULL) 357 return (-1); 358 if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) < 0) { 359 /* 360 * Pools configuration file is corrupted; allow logins. 361 */ 362 pool_conf_free(conf); 363 return (0); 364 } 365 if (pool_name != NULL && pool_get_pool(conf, pool_name) != NULL) { 366 /* 367 * There was a project.pool entry, and the pool it refers to 368 * is a valid (active) pool. 369 */ 370 (void) pool_conf_close(conf); 371 pool_conf_free(conf); 372 if (pool_set_binding(pool_name, P_PID, pid) != PO_SUCCESS) { 373 if (pool_error() != POE_SYSTEM) 374 errno = EINVAL; 375 return (-1); 376 } 377 return (0); 378 } 379 380 /* 381 * Bind to the pool with 'pool.default' = 'true' if 382 * 'system.bind-default' = 'true'. 383 */ 384 if ((pvals[0] = pool_value_alloc()) == NULL) { 385 pool_conf_close(conf); 386 pool_conf_free(conf); 387 return (-1); 388 } 389 if (!force && pool_get_property(conf, pool_conf_to_elem(conf), 390 "system.bind-default", pvals[0]) != POC_BOOL || 391 pool_value_get_bool(pvals[0], &bval) != PO_SUCCESS || 392 bval == PO_FALSE) { 393 pool_value_free(pvals[0]); 394 pool_conf_close(conf); 395 pool_conf_free(conf); 396 errno = pool_name == NULL ? EACCES : ESRCH; 397 return (-1); 398 } 399 (void) pool_value_set_name(pvals[0], "pool.default"); 400 pool_value_set_bool(pvals[0], PO_TRUE); 401 if ((pools = pool_query_pools(conf, &nelem, pvals)) == NULL) { 402 /* 403 * No default pools exist. 404 */ 405 pool_value_free(pvals[0]); 406 pool_conf_close(conf); 407 pool_conf_free(conf); 408 errno = pool_name == NULL ? EACCES : ESRCH; 409 return (-1); 410 } 411 if (nelem != 1 || 412 pool_get_property(conf, pool_to_elem(conf, pools[0]), "pool.name", 413 pvals[0]) != POC_STRING) { 414 /* 415 * Configuration is invalid. 416 */ 417 free(pools); 418 pool_value_free(pvals[0]); 419 (void) pool_conf_close(conf); 420 pool_conf_free(conf); 421 return (0); 422 } 423 free(pools); 424 (void) pool_conf_close(conf); 425 pool_conf_free(conf); 426 (void) pool_value_get_string(pvals[0], &nm); 427 if (pool_set_binding(nm, P_PID, pid) != PO_SUCCESS) { 428 if (pool_error() != POE_SYSTEM) 429 errno = EINVAL; 430 retval = -1; 431 } else { 432 retval = 0; 433 } 434 pool_value_free(pvals[0]); 435 return (retval); 436 } 437 438 /* 439 * Changes the assigned project, task and resource pool of a stopped target 440 * process. 441 * 442 * We may not have access to the project table if our target process is in 443 * getprojbyname()'s execution path. Similarly, we may not be able to get user 444 * information if the target process is in getpwnam()'s execution path. Thus we 445 * give the caller the option of skipping these checks by providing a pointer to 446 * a pre-validated project structure in proj (whose name matches project_name) 447 * and taking responsibility for ensuring that the target process' owner is a 448 * member of the target project. 449 * 450 * Callers of this function should always provide a pre-validated project 451 * structure in proj unless they can be sure that the target process will never 452 * be in setproject_proc()'s execution path. 453 */ 454 455 projid_t 456 setproject_proc(const char *project_name, const char *user_name, int flags, 457 pid_t pid, struct ps_prochandle *Pr, struct project *proj) 458 { 459 char pwdbuf[NSS_BUFLEN_PASSWD]; 460 char prbuf[PROJECT_BUFSZ]; 461 projid_t projid; 462 struct passwd pwd; 463 int i; 464 int unknown = 0; 465 int ret = 0; 466 kva_t *kv_array; 467 struct project local_proj; /* space to store proj if not provided */ 468 469 if (project_name != NULL) { 470 /* 471 * Sanity checks. 472 */ 473 if (strcmp(project_name, "") == 0 || 474 user_name == NULL) { 475 errno = EINVAL; 476 return (SETPROJ_ERR_TASK); 477 } 478 479 /* 480 * If proj is NULL, acquire project information to ensure that 481 * project_name is a valid project, and confirm that user_name 482 * exists and is a member of the specified project. 483 */ 484 if (proj == NULL) { 485 if ((proj = getprojbyname(project_name, &local_proj, 486 prbuf, PROJECT_BUFSZ)) == NULL) { 487 errno = ESRCH; 488 return (SETPROJ_ERR_TASK); 489 } 490 491 if (getpwnam_r(user_name, &pwd, 492 pwdbuf, NSS_BUFLEN_PASSWD) == NULL) { 493 errno = ESRCH; 494 return (SETPROJ_ERR_TASK); 495 } 496 /* 497 * Root can join any project. 498 */ 499 if (pwd.pw_uid != (uid_t)0 && 500 !inproj(user_name, project_name, prbuf, 501 PROJECT_BUFSZ)) { 502 errno = ESRCH; 503 return (SETPROJ_ERR_TASK); 504 } 505 } 506 projid = proj->pj_projid; 507 } else { 508 projid = getprojid(); 509 } 510 511 /* 512 * Only bind to a pool if pools are configured. 513 */ 514 if (pools_enabled() == 1) { 515 const char *pool_name = NULL; 516 char *old_pool_name; 517 int taskflags = flags; 518 /* 519 * Attempt to bind to pool before calling 520 * settaskid(). 521 */ 522 if ((kv_array = _str2kva(proj->pj_attr, KV_ASSIGN, 523 KV_DELIMITER)) != NULL) { 524 for (i = 0; i < kv_array->length; i++) { 525 if (strcmp(kv_array->data[i].key, 526 "project.pool") == 0) { 527 pool_name = kv_array->data[i].value; 528 break; 529 } 530 if (strcmp(kv_array->data[i].key, 531 "task.final") == 0) { 532 taskflags |= TASK_FINAL; 533 } 534 } 535 } 536 537 old_pool_name = pool_get_binding(pid); 538 539 /* 540 * If parent is not bound to the default pool, then we want 541 * to preserve same binding as parent. 542 */ 543 if (pool_name != NULL && bind_to_pool(pool_name, pid, 0) != 0) { 544 if (old_pool_name) 545 free(old_pool_name); 546 _kva_free(kv_array); 547 return (SETPROJ_ERR_POOL); 548 } 549 if (pr_settaskid(Pr, projid, taskflags) == -1) { 550 int saved_errno = errno; 551 552 /* 553 * Undo pool binding. 554 */ 555 (void) bind_to_pool(old_pool_name, pid, 1); 556 if (old_pool_name) 557 free(old_pool_name); 558 _kva_free(kv_array); 559 /* 560 * Restore errno 561 */ 562 errno = saved_errno; 563 return (SETPROJ_ERR_TASK); 564 } 565 if (old_pool_name) 566 free(old_pool_name); 567 } else { 568 /* 569 * Pools are not configured, so simply create new task. 570 */ 571 if (pr_settaskid(Pr, projid, flags) == -1) 572 return (SETPROJ_ERR_TASK); 573 kv_array = _str2kva(proj->pj_attr, KV_ASSIGN, KV_DELIMITER); 574 } 575 576 if (project_name == NULL) { 577 /* 578 * In the case that we are starting a new task in the 579 * current project, we are finished, since the current 580 * resource controls will still apply. (Implicit behaviour: 581 * a project must be entirely logged out before name 582 * service changes will take effect.) 583 */ 584 _kva_free(kv_array); 585 return (projid); 586 } 587 588 if (kv_array == NULL) 589 return (0); 590 591 for (i = 0; i < kv_array->length; i++) { 592 /* 593 * Providing a special, i.e. a non-resource control, key? Then 594 * parse that key here and end with "continue;". 595 */ 596 597 /* 598 * For generic bindings, the kernel performs the binding, as 599 * these are resource controls advertised by kernel subsystems. 600 */ 601 602 /* 603 * Check for known attribute name. 604 */ 605 errno = 0; 606 if (rctl_walk(rctlwalkfunc, (void *)kv_array->data[i].key) 607 == 0) 608 continue; 609 if (errno) { 610 _kva_free(kv_array); 611 return (SETPROJ_ERR_TASK); 612 } 613 614 ret = rctl_set(kv_array->data[i].key, 615 kv_array->data[i].value, Pr); 616 617 if (ret && unknown == 0) { 618 /* 619 * We only report the first failure. 620 */ 621 unknown = i + 1; 622 } 623 624 if (ret && ret != SETFAILED) { 625 /* 626 * We abort if we couldn't set a component, but if 627 * it's merely that the system didn't recognize it, we 628 * continue, as this could be a third party attribute. 629 */ 630 break; 631 } 632 } 633 _kva_free(kv_array); 634 635 return (unknown); 636 } 637 638 projid_t 639 setproject(const char *project_name, const char *user_name, int flags) 640 { 641 return (setproject_proc(project_name, user_name, flags, P_MYID, NULL, 642 NULL)); 643 } 644 645 646 priv_set_t * 647 setproject_initpriv(void) 648 { 649 static priv_t taskpriv = PRIV_PROC_TASKID; 650 static priv_t rctlpriv = PRIV_SYS_RESOURCE; 651 static priv_t poolpriv = PRIV_SYS_RES_CONFIG; 652 static priv_t schedpriv = PRIV_PROC_PRIOCNTL; 653 int res; 654 655 priv_set_t *nset; 656 657 if (getzoneid() == GLOBAL_ZONEID) { 658 res = __init_suid_priv(0, taskpriv, rctlpriv, poolpriv, 659 schedpriv, (char *)NULL); 660 } else { 661 res = __init_suid_priv(0, taskpriv, rctlpriv, (char *)NULL); 662 } 663 664 if (res != 0) 665 return (NULL); 666 667 nset = priv_allocset(); 668 if (nset != NULL) { 669 priv_emptyset(nset); 670 (void) priv_addset(nset, taskpriv); 671 (void) priv_addset(nset, rctlpriv); 672 /* 673 * Only need these if we need to change pools, which can 674 * only happen if the target is in the global zone. Rather 675 * than checking the target's zone just check our own 676 * (since if we're in a non-global zone we won't be able 677 * to control processes in other zones). 678 */ 679 if (getzoneid() == GLOBAL_ZONEID) { 680 (void) priv_addset(nset, poolpriv); 681 (void) priv_addset(nset, schedpriv); 682 } 683 } 684 return (nset); 685 } 686