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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/types.h> 30 31 #include <sys/cmn_err.h> 32 #include <sys/cred.h> 33 #include <sys/errno.h> 34 #include <sys/rctl.h> 35 #include <sys/rctl_impl.h> 36 #include <sys/strlog.h> 37 #include <sys/syslog.h> 38 #include <sys/sysmacros.h> 39 #include <sys/systm.h> 40 #include <sys/policy.h> 41 #include <sys/proc.h> 42 #include <sys/task.h> 43 44 /* 45 * setrctl(2), getrctl(2), and private rctlsys(2*) system calls 46 * 47 * Resource control block (rctlblk_ptr_t, rctl_opaque_t) 48 * The resource control system call interfaces present the resource control 49 * values and flags via the resource control block abstraction, made manifest 50 * via an opaque data type with strict type definitions. Keeping the formal 51 * definitions in the rcontrol block allows us to be clever in the kernel, 52 * combining attributes where appropriate in the current implementation while 53 * preserving binary compatibility in the face of implementation changes. 54 */ 55 56 #define RBX_TO_BLK 0x1 57 #define RBX_FROM_BLK 0x2 58 #define RBX_VAL 0x4 59 #define RBX_CTL 0x8 60 61 static void 62 rctlsys_rblk_xfrm(rctl_opaque_t *blk, rctl_dict_entry_t *rde, 63 rctl_val_t *val, int flags) 64 { 65 if (flags & RBX_FROM_BLK) { 66 if (flags & RBX_VAL) { 67 /* 68 * Firing time cannot be set. 69 */ 70 val->rcv_privilege = blk->rcq_privilege; 71 val->rcv_value = blk->rcq_value; 72 val->rcv_flagaction = blk->rcq_local_flagaction; 73 val->rcv_action_signal = blk->rcq_local_signal; 74 val->rcv_action_recip_pid = 75 blk->rcq_local_recipient_pid; 76 } 77 if (flags & RBX_CTL) { 78 rde->rcd_flagaction = blk->rcq_global_flagaction; 79 rde->rcd_syslog_level = blk->rcq_global_syslog_level; 80 81 /* 82 * Because the strlog() interface supports fewer options 83 * than are made available via the syslog() interface to 84 * userland, we map the syslog level down to a smaller 85 * set of distinct logging behaviours. 86 */ 87 rde->rcd_strlog_flags = 0; 88 switch (blk->rcq_global_syslog_level) { 89 case LOG_EMERG: 90 case LOG_ALERT: 91 case LOG_CRIT: 92 rde->rcd_strlog_flags |= SL_CONSOLE; 93 /*FALLTHROUGH*/ 94 case LOG_ERR: 95 rde->rcd_strlog_flags |= SL_ERROR; 96 /*FALLTHROUGH*/ 97 case LOG_WARNING: 98 rde->rcd_strlog_flags |= SL_WARN; 99 break; 100 case LOG_NOTICE: 101 rde->rcd_strlog_flags |= SL_CONSOLE; 102 /*FALLTHROUGH*/ 103 case LOG_INFO: /* informational */ 104 case LOG_DEBUG: /* debug-level messages */ 105 default: 106 rde->rcd_strlog_flags |= SL_NOTE; 107 break; 108 } 109 } 110 } else { 111 bzero(blk, sizeof (rctl_opaque_t)); 112 if (flags & RBX_VAL) { 113 blk->rcq_privilege = val->rcv_privilege; 114 blk->rcq_value = val->rcv_value; 115 blk->rcq_enforced_value = rctl_model_value(rde, 116 curproc, val->rcv_value); 117 blk->rcq_local_flagaction = val->rcv_flagaction; 118 blk->rcq_local_signal = val->rcv_action_signal; 119 blk->rcq_firing_time = val->rcv_firing_time; 120 blk->rcq_local_recipient_pid = 121 val->rcv_action_recip_pid; 122 } 123 if (flags & RBX_CTL) { 124 blk->rcq_global_flagaction = rde->rcd_flagaction; 125 blk->rcq_global_syslog_level = rde->rcd_syslog_level; 126 } 127 } 128 } 129 130 /* 131 * int rctl_invalid_value(rctl_dict_entry_t *, rctl_val_t *) 132 * 133 * Overview 134 * Perform basic validation of proposed new resource control value against the 135 * global properties set on the control. Any system call operation presented 136 * with an invalid resource control value should return -1 and set errno to 137 * EINVAL. 138 * 139 * Return values 140 * 0 if valid, 1 if invalid. 141 * 142 * Caller's context 143 * No restriction on context. 144 */ 145 int 146 rctl_invalid_value(rctl_dict_entry_t *rde, rctl_val_t *rval) 147 { 148 rctl_val_t *sys_rval; 149 150 if (rval->rcv_privilege != RCPRIV_BASIC && 151 rval->rcv_privilege != RCPRIV_PRIVILEGED && 152 rval->rcv_privilege != RCPRIV_SYSTEM) 153 return (1); 154 155 if (rval->rcv_flagaction & ~RCTL_LOCAL_MASK) 156 return (1); 157 158 if (rval->rcv_privilege == RCPRIV_BASIC && 159 (rde->rcd_flagaction & RCTL_GLOBAL_NOBASIC) != 0) 160 return (1); 161 162 if ((rval->rcv_flagaction & RCTL_LOCAL_DENY) == 0 && 163 (rde->rcd_flagaction & RCTL_GLOBAL_DENY_ALWAYS) != 0) 164 return (1); 165 166 if ((rval->rcv_flagaction & RCTL_LOCAL_DENY) && 167 (rde->rcd_flagaction & RCTL_GLOBAL_DENY_NEVER)) 168 return (1); 169 170 if ((rval->rcv_flagaction & RCTL_LOCAL_SIGNAL) && 171 (rde->rcd_flagaction & RCTL_GLOBAL_SIGNAL_NEVER)) 172 return (1); 173 174 if ((rval->rcv_flagaction & RCTL_LOCAL_SIGNAL) && 175 rval->rcv_action_signal == 0) 176 return (1); 177 178 if (rval->rcv_action_signal == SIGXCPU && 179 (rde->rcd_flagaction & RCTL_GLOBAL_CPU_TIME) == 0) 180 return (1); 181 else if (rval->rcv_action_signal == SIGXFSZ && 182 (rde->rcd_flagaction & RCTL_GLOBAL_FILE_SIZE) == 0) 183 return (1); 184 else if (rval->rcv_action_signal != SIGHUP && 185 rval->rcv_action_signal != SIGABRT && 186 rval->rcv_action_signal != SIGKILL && 187 rval->rcv_action_signal != SIGTERM && 188 rval->rcv_action_signal != SIGSTOP && 189 rval->rcv_action_signal != SIGXCPU && 190 rval->rcv_action_signal != SIGXFSZ && 191 rval->rcv_action_signal != SIGXRES && 192 rval->rcv_action_signal != 0) /* That is, no signal is ok. */ 193 return (1); 194 195 sys_rval = rde->rcd_default_value; 196 while (sys_rval->rcv_privilege != RCPRIV_SYSTEM) 197 sys_rval = sys_rval->rcv_next; 198 199 if (rval->rcv_value > sys_rval->rcv_value) 200 return (1); 201 202 return (0); 203 } 204 205 /* 206 * static long rctlsys_get(char *name, rctl_opaque_t *old_rblk, 207 * rctl_opaque_t *new_rblk, int flags) 208 * 209 * Overview 210 * rctlsys_get() is the implementation of the core logic of getrctl(2), the 211 * public system call for fetching resource control values. Two mutually 212 * exclusive flag values are supported: RCTL_FIRST and RCTL_NEXT. When 213 * RCTL_FIRST is presented, the value of old_rblk is ignored, and the first 214 * value in the resource control value sequence for the named control is 215 * transformed and placed in the user memory location at new_rblk. In the 216 * RCTL_NEXT case, the value of old_rblk is examined, and the next value in 217 * the sequence is transformed and placed at new_rblk. 218 */ 219 static long 220 rctlsys_get(char *name, rctl_opaque_t *old_rblk, rctl_opaque_t *new_rblk, 221 int flags) 222 { 223 rctl_val_t *nval; 224 rctl_opaque_t *nblk; 225 rctl_hndl_t hndl; 226 char *kname; 227 size_t klen; 228 rctl_dict_entry_t *krde; 229 int ret; 230 int action = flags & (~RCTLSYS_ACTION_MASK); 231 232 if (flags & (~RCTLSYS_MASK)) 233 return (set_errno(EINVAL)); 234 235 if (action != RCTL_FIRST && action != RCTL_NEXT && 236 action != RCTL_USAGE) 237 return (set_errno(EINVAL)); 238 239 if (new_rblk == NULL || name == NULL) 240 return (set_errno(EFAULT)); 241 242 kname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 243 krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP); 244 245 if (copyinstr(name, kname, MAXPATHLEN, &klen) != 0) { 246 kmem_free(kname, MAXPATHLEN); 247 kmem_free(krde, sizeof (rctl_dict_entry_t)); 248 return (set_errno(EFAULT)); 249 } 250 251 if ((hndl = rctl_hndl_lookup(kname)) == -1) { 252 kmem_free(kname, MAXPATHLEN); 253 kmem_free(krde, sizeof (rctl_dict_entry_t)); 254 return (set_errno(EINVAL)); 255 } 256 257 if (rctl_global_get(kname, krde) == -1) { 258 kmem_free(kname, MAXPATHLEN); 259 kmem_free(krde, sizeof (rctl_dict_entry_t)); 260 return (set_errno(ESRCH)); 261 } 262 263 kmem_free(kname, MAXPATHLEN); 264 265 nval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); 266 267 if (action == RCTL_USAGE) { 268 kmem_cache_free(rctl_val_cache, nval); 269 kmem_free(krde, sizeof (rctl_dict_entry_t)); 270 return (set_errno(ENOTSUP)); 271 } else if (action == RCTL_FIRST) { 272 273 mutex_enter(&curproc->p_lock); 274 if (ret = rctl_local_get(hndl, NULL, nval, curproc)) { 275 mutex_exit(&curproc->p_lock); 276 kmem_cache_free(rctl_val_cache, nval); 277 kmem_free(krde, sizeof (rctl_dict_entry_t)); 278 return (set_errno(ret)); 279 } 280 mutex_exit(&curproc->p_lock); 281 } else { 282 /* 283 * RCTL_NEXT 284 */ 285 rctl_val_t *oval; 286 rctl_opaque_t *oblk; 287 288 oblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP); 289 290 if (copyin(old_rblk, oblk, sizeof (rctl_opaque_t)) == -1) { 291 kmem_cache_free(rctl_val_cache, nval); 292 kmem_free(oblk, sizeof (rctl_opaque_t)); 293 kmem_free(krde, sizeof (rctl_dict_entry_t)); 294 return (set_errno(EFAULT)); 295 } 296 297 oval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); 298 299 rctlsys_rblk_xfrm(oblk, NULL, oval, RBX_FROM_BLK | RBX_VAL); 300 mutex_enter(&curproc->p_lock); 301 ret = rctl_local_get(hndl, oval, nval, curproc); 302 mutex_exit(&curproc->p_lock); 303 304 kmem_cache_free(rctl_val_cache, oval); 305 kmem_free(oblk, sizeof (rctl_opaque_t)); 306 307 if (ret != 0) { 308 kmem_cache_free(rctl_val_cache, nval); 309 kmem_free(krde, sizeof (rctl_dict_entry_t)); 310 return (set_errno(ret)); 311 } 312 } 313 314 nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP); 315 316 rctlsys_rblk_xfrm(nblk, krde, nval, RBX_TO_BLK | RBX_VAL | RBX_CTL); 317 318 kmem_free(krde, sizeof (rctl_dict_entry_t)); 319 kmem_cache_free(rctl_val_cache, nval); 320 321 if (copyout(nblk, new_rblk, sizeof (rctl_opaque_t)) == -1) { 322 kmem_free(nblk, sizeof (rctl_opaque_t)); 323 return (set_errno(EFAULT)); 324 } 325 326 kmem_free(nblk, sizeof (rctl_opaque_t)); 327 328 return (0); 329 } 330 331 /* 332 * static long rctlsys_set(char *name, rctl_opaque_t *old_rblk, 333 * rctl_opaque_t *new_rblk, int flags) 334 * 335 * Overview 336 * rctlsys_set() is the implementation of the core login of setrctl(2), which 337 * allows the establishment of resource control values. Flags may take on any 338 * of three exclusive values: RCTL_INSERT, RCTL_DELETE, and RCTL_REPLACE. 339 * RCTL_INSERT ignores old_rblk and inserts the value in the appropriate 340 * position in the ordered sequence of resource control values. RCTL_DELETE 341 * ignores old_rblk and deletes the first resource control value matching 342 * (value, priority) in the given resource block. If no matching value is 343 * found, -1 is returned and errno is set to ENOENT. Finally, in the case of 344 * RCTL_REPLACE, old_rblk is used to match (value, priority); the matching 345 * resource control value in the sequence is replaced with the contents of 346 * new_rblk. Again, if no match is found, -1 is returned and errno is set to 347 * ENOENT. 348 * 349 * rctlsys_set() causes a cursor test, which can reactivate resource controls 350 * that have previously fired. 351 */ 352 static long 353 rctlsys_set(char *name, rctl_opaque_t *old_rblk, rctl_opaque_t *new_rblk, 354 int flags) 355 { 356 rctl_val_t *nval; 357 rctl_dict_entry_t *rde; 358 rctl_opaque_t *nblk; 359 rctl_hndl_t hndl; 360 char *kname; 361 size_t klen; 362 long ret = 0; 363 proc_t *pp = NULL; 364 pid_t pid; 365 int action = flags & (~RCTLSYS_ACTION_MASK); 366 rctl_val_t *oval; 367 rctl_val_t *rval1; 368 rctl_val_t *rval2; 369 rctl_val_t *tval; 370 rctl_opaque_t *oblk; 371 372 if (flags & (~RCTLSYS_MASK)) 373 return (set_errno(EINVAL)); 374 375 if (action != RCTL_INSERT && 376 action != RCTL_DELETE && 377 action != RCTL_REPLACE) 378 return (set_errno(EINVAL)); 379 380 if (new_rblk == NULL || name == NULL) 381 return (set_errno(EFAULT)); 382 383 kname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 384 if (copyinstr(name, kname, MAXPATHLEN, &klen) != 0) { 385 kmem_free(kname, MAXPATHLEN); 386 return (set_errno(EFAULT)); 387 } 388 389 if ((hndl = rctl_hndl_lookup(kname)) == -1) { 390 kmem_free(kname, MAXPATHLEN); 391 return (set_errno(EINVAL)); 392 } 393 394 kmem_free(kname, MAXPATHLEN); 395 396 rde = rctl_dict_lookup_hndl(hndl); 397 398 nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP); 399 400 if (copyin(new_rblk, nblk, sizeof (rctl_opaque_t)) == -1) { 401 kmem_free(nblk, sizeof (rctl_opaque_t)); 402 return (set_errno(EFAULT)); 403 } 404 405 nval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); 406 407 rctlsys_rblk_xfrm(nblk, NULL, nval, RBX_FROM_BLK | RBX_VAL); 408 409 if (rctl_invalid_value(rde, nval)) { 410 kmem_free(nblk, sizeof (rctl_opaque_t)); 411 kmem_cache_free(rctl_val_cache, nval); 412 return (set_errno(EINVAL)); 413 } 414 415 /* allocate what we might need before potentially grabbing p_lock */ 416 oblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP); 417 oval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); 418 rval1 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); 419 rval2 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP); 420 421 if (nval->rcv_privilege == RCPRIV_BASIC) { 422 if (flags & RCTL_USE_RECIPIENT_PID) { 423 pid = nval->rcv_action_recip_pid; 424 425 /* case for manipulating rctl values on other procs */ 426 if (pid != curproc->p_pid) { 427 /* cannot be other pid on process rctls */ 428 if (rde->rcd_entity == RCENTITY_PROCESS) { 429 ret = set_errno(EINVAL); 430 goto rctlsys_out; 431 } 432 /* 433 * must have privilege to manipulate controls 434 * on other processes 435 */ 436 if (secpolicy_rctlsys(CRED(), B_FALSE) != 0) { 437 ret = set_errno(EACCES); 438 goto rctlsys_out; 439 } 440 441 pid = nval->rcv_action_recip_pid; 442 mutex_enter(&pidlock); 443 pp = prfind(pid); 444 if (!pp) { 445 mutex_exit(&pidlock); 446 ret = set_errno(ESRCH); 447 goto rctlsys_out; 448 } 449 450 /* 451 * idle or zombie procs have either not yet 452 * set up their rctls or have already done 453 * their rctl_set_tearoff's. 454 */ 455 if (pp->p_stat == SZOMB || 456 pp->p_stat == SIDL) { 457 mutex_exit(&pidlock); 458 ret = set_errno(ESRCH); 459 goto rctlsys_out; 460 } 461 462 /* 463 * hold this pp's p_lock to ensure that 464 * it does not do it's rctl_set_tearoff 465 * If we did not do this, we could 466 * potentially add rctls to the entity 467 * with a recipient that is a process 468 * that has exited. 469 */ 470 mutex_enter(&pp->p_lock); 471 mutex_exit(&pidlock); 472 473 /* 474 * We know that curproc's task, project, 475 * and zone pointers will not change 476 * because functions that change them 477 * call holdlwps(SHOLDFORK1) first. 478 */ 479 480 /* 481 * verify that the found pp is in the 482 * current task. If it is, then it 483 * is also within the current project 484 * and zone. 485 */ 486 if (rde->rcd_entity == RCENTITY_TASK && 487 pp->p_task != curproc->p_task) { 488 ret = set_errno(ESRCH); 489 goto rctlsys_out; 490 } 491 492 ASSERT(pp->p_task->tk_proj == 493 curproc->p_task->tk_proj); 494 ASSERT(pp->p_zone == curproc->p_zone); 495 496 497 nval->rcv_action_recipient = pp; 498 nval->rcv_action_recip_pid = pid; 499 500 } else { 501 /* for manipulating rctl values on this proc */ 502 mutex_enter(&curproc->p_lock); 503 pp = curproc; 504 nval->rcv_action_recipient = curproc; 505 nval->rcv_action_recip_pid = curproc->p_pid; 506 } 507 508 } else { 509 /* RCTL_USE_RECIPIENT_PID not set, use this proc */ 510 mutex_enter(&curproc->p_lock); 511 pp = curproc; 512 nval->rcv_action_recipient = curproc; 513 nval->rcv_action_recip_pid = curproc->p_pid; 514 } 515 516 } else { 517 /* privileged controls have no recipient pid */ 518 mutex_enter(&curproc->p_lock); 519 pp = curproc; 520 nval->rcv_action_recipient = NULL; 521 nval->rcv_action_recip_pid = -1; 522 } 523 524 nval->rcv_firing_time = 0; 525 526 if (action == RCTL_REPLACE) { 527 528 if (copyin(old_rblk, oblk, sizeof (rctl_opaque_t)) == -1) { 529 ret = set_errno(EFAULT); 530 goto rctlsys_out; 531 } 532 533 rctlsys_rblk_xfrm(oblk, NULL, oval, RBX_FROM_BLK | RBX_VAL); 534 535 if (rctl_invalid_value(rde, oval)) { 536 ret = set_errno(EINVAL); 537 goto rctlsys_out; 538 } 539 540 if (oval->rcv_privilege == RCPRIV_BASIC) { 541 if (!(flags & RCTL_USE_RECIPIENT_PID)) { 542 oval->rcv_action_recipient = curproc; 543 oval->rcv_action_recip_pid = curproc->p_pid; 544 } 545 } else { 546 oval->rcv_action_recipient = NULL; 547 oval->rcv_action_recip_pid = -1; 548 } 549 550 /* 551 * Find the real value we're attempting to replace on the 552 * sequence, rather than trusting the one delivered from 553 * userland. 554 */ 555 if (ret = rctl_local_get(hndl, NULL, rval1, pp)) { 556 (void) set_errno(ret); 557 goto rctlsys_out; 558 } 559 560 do { 561 if (rval1->rcv_privilege == RCPRIV_SYSTEM || 562 rctl_val_cmp(oval, rval1, 0) == 0) 563 break; 564 565 tval = rval1; 566 rval1 = rval2; 567 rval2 = tval; 568 } while (rctl_local_get(hndl, rval2, rval1, pp) == 0); 569 570 if (rval1->rcv_privilege == RCPRIV_SYSTEM) { 571 if (rctl_val_cmp(oval, rval1, 1) == 0) 572 ret = set_errno(EPERM); 573 else 574 ret = set_errno(ESRCH); 575 576 goto rctlsys_out; 577 } 578 579 bcopy(rval1, oval, sizeof (rctl_val_t)); 580 581 /* 582 * System controls are immutable. 583 */ 584 if (nval->rcv_privilege == RCPRIV_SYSTEM) { 585 ret = set_errno(EPERM); 586 goto rctlsys_out; 587 } 588 589 /* 590 * Only privileged processes in the global zone can modify 591 * privileged rctls of type RCENTITY_ZONE; replacing privileged 592 * controls with basic ones are not allowed either. Lowering a 593 * lowerable one might be OK for privileged processes in a 594 * non-global zone, but lowerable rctls probably don't make 595 * sense for zones (hence, not modifiable from within a zone). 596 */ 597 if (rde->rcd_entity == RCENTITY_ZONE && 598 (nval->rcv_privilege == RCPRIV_PRIVILEGED || 599 oval->rcv_privilege == RCPRIV_PRIVILEGED) && 600 secpolicy_rctlsys(CRED(), B_TRUE) != 0) { 601 ret = set_errno(EACCES); 602 goto rctlsys_out; 603 } 604 605 /* 606 * Must be privileged to replace a privileged control with 607 * a basic one. 608 */ 609 if (oval->rcv_privilege == RCPRIV_PRIVILEGED && 610 nval->rcv_privilege != RCPRIV_PRIVILEGED && 611 secpolicy_rctlsys(CRED(), B_FALSE) != 0) { 612 ret = set_errno(EACCES); 613 goto rctlsys_out; 614 } 615 616 /* 617 * Must have lowerable global property for non-privileged 618 * to lower the value of a privileged control; otherwise must 619 * have sufficient privileges to modify privileged controls 620 * at all. 621 */ 622 if (oval->rcv_privilege == RCPRIV_PRIVILEGED && 623 nval->rcv_privilege == RCPRIV_PRIVILEGED && 624 ((((rde->rcd_flagaction & RCTL_GLOBAL_LOWERABLE) == 0) || 625 oval->rcv_flagaction != nval->rcv_flagaction || 626 oval->rcv_action_signal != nval->rcv_action_signal || 627 oval->rcv_value < nval->rcv_value)) && 628 secpolicy_rctlsys(CRED(), B_FALSE) != 0) { 629 ret = set_errno(EACCES); 630 goto rctlsys_out; 631 } 632 633 if (ret = rctl_local_replace(hndl, oval, nval, pp)) { 634 (void) set_errno(ret); 635 goto rctlsys_out; 636 } 637 638 /* ensure that nval is not freed */ 639 nval = NULL; 640 641 } else if (action == RCTL_INSERT) { 642 /* 643 * System controls are immutable. 644 */ 645 if (nval->rcv_privilege == RCPRIV_SYSTEM) { 646 ret = set_errno(EPERM); 647 goto rctlsys_out; 648 } 649 650 /* 651 * Only privileged processes in the global zone may add 652 * privileged zone.* rctls. Only privileged processes 653 * may add other privileged rctls. 654 */ 655 if (nval->rcv_privilege == RCPRIV_PRIVILEGED) { 656 if ((rde->rcd_entity == RCENTITY_ZONE && 657 secpolicy_rctlsys(CRED(), B_TRUE) != 0) || 658 (rde->rcd_entity != RCENTITY_ZONE && 659 secpolicy_rctlsys(CRED(), B_FALSE) != 0)) { 660 ret = set_errno(EACCES); 661 goto rctlsys_out; 662 } 663 } 664 665 /* 666 * Only one basic control is allowed per rctl. 667 * If a basic control is being inserted, delete 668 * any other basic control. 669 */ 670 if ((nval->rcv_privilege == RCPRIV_BASIC) && 671 (rctl_local_get(hndl, NULL, rval1, pp) == 0)) { 672 do { 673 if (rval1->rcv_privilege == RCPRIV_BASIC && 674 rval1->rcv_action_recipient == curproc) { 675 (void) rctl_local_delete(hndl, rval1, 676 pp); 677 if (rctl_local_get(hndl, NULL, rval1, 678 pp) != 0) 679 break; 680 } 681 682 tval = rval1; 683 rval1 = rval2; 684 rval2 = tval; 685 } while (rctl_local_get(hndl, rval2, rval1, pp) 686 == 0); 687 } 688 689 690 if (ret = rctl_local_insert(hndl, nval, pp)) { 691 (void) set_errno(ret); 692 goto rctlsys_out; 693 } 694 695 /* ensure that nval is not freed */ 696 nval = NULL; 697 698 } else { 699 /* 700 * RCTL_DELETE 701 */ 702 if (nval->rcv_privilege == RCPRIV_SYSTEM) { 703 ret = set_errno(EPERM); 704 goto rctlsys_out; 705 } 706 707 if (nval->rcv_privilege == RCPRIV_PRIVILEGED) { 708 if ((rde->rcd_entity == RCENTITY_ZONE && 709 secpolicy_rctlsys(CRED(), B_TRUE) != 0) || 710 (rde->rcd_entity != RCENTITY_ZONE && 711 secpolicy_rctlsys(CRED(), B_FALSE) != 0)) { 712 ret = set_errno(EACCES); 713 goto rctlsys_out; 714 } 715 } 716 717 if (ret = rctl_local_delete(hndl, nval, pp)) { 718 (void) set_errno(ret); 719 goto rctlsys_out; 720 } 721 } 722 723 rctlsys_out: 724 725 if (pp) 726 mutex_exit(&pp->p_lock); 727 728 kmem_free(nblk, sizeof (rctl_opaque_t)); 729 kmem_free(oblk, sizeof (rctl_opaque_t)); 730 731 /* only free nval if we did not rctl_local_insert it */ 732 if (nval) 733 kmem_cache_free(rctl_val_cache, nval); 734 735 kmem_cache_free(rctl_val_cache, oval); 736 kmem_cache_free(rctl_val_cache, rval1); 737 kmem_cache_free(rctl_val_cache, rval2); 738 739 return (ret); 740 } 741 742 static long 743 rctlsys_lst(char *ubuf, size_t ubufsz) 744 { 745 char *kbuf; 746 size_t kbufsz; 747 748 kbufsz = rctl_build_name_buf(&kbuf); 749 750 if (kbufsz <= ubufsz && 751 copyout(kbuf, ubuf, kbufsz) != 0) { 752 kmem_free(kbuf, kbufsz); 753 return (set_errno(EFAULT)); 754 } 755 756 kmem_free(kbuf, kbufsz); 757 758 return (kbufsz); 759 } 760 761 static long 762 rctlsys_ctl(char *name, rctl_opaque_t *rblk, int flags) 763 { 764 rctl_dict_entry_t *krde; 765 rctl_opaque_t *krblk; 766 char *kname; 767 size_t klen; 768 769 kname = kmem_alloc(MAXPATHLEN, KM_SLEEP); 770 771 if (name == NULL || copyinstr(name, kname, MAXPATHLEN, &klen) != 0) { 772 kmem_free(kname, MAXPATHLEN); 773 return (set_errno(EFAULT)); 774 } 775 776 switch (flags) { 777 case RCTLCTL_GET: 778 krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP); 779 krblk = kmem_zalloc(sizeof (rctl_opaque_t), KM_SLEEP); 780 781 if (rctl_global_get(kname, krde) == -1) { 782 kmem_free(krde, sizeof (rctl_dict_entry_t)); 783 kmem_free(krblk, sizeof (rctl_opaque_t)); 784 kmem_free(kname, MAXPATHLEN); 785 return (set_errno(ESRCH)); 786 } 787 788 rctlsys_rblk_xfrm(krblk, krde, NULL, RBX_TO_BLK | RBX_CTL); 789 790 if (copyout(krblk, rblk, sizeof (rctl_opaque_t)) != 0) { 791 kmem_free(krde, sizeof (rctl_dict_entry_t)); 792 kmem_free(krblk, sizeof (rctl_opaque_t)); 793 kmem_free(kname, MAXPATHLEN); 794 return (set_errno(EFAULT)); 795 } 796 797 kmem_free(krde, sizeof (rctl_dict_entry_t)); 798 kmem_free(krblk, sizeof (rctl_opaque_t)); 799 kmem_free(kname, MAXPATHLEN); 800 break; 801 case RCTLCTL_SET: 802 if (secpolicy_rctlsys(CRED(), B_TRUE) != 0) { 803 kmem_free(kname, MAXPATHLEN); 804 return (set_errno(EPERM)); 805 } 806 807 krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP); 808 krblk = kmem_zalloc(sizeof (rctl_opaque_t), KM_SLEEP); 809 810 if (rctl_global_get(kname, krde) == -1) { 811 kmem_free(krde, sizeof (rctl_dict_entry_t)); 812 kmem_free(krblk, sizeof (rctl_opaque_t)); 813 kmem_free(kname, MAXPATHLEN); 814 return (set_errno(ESRCH)); 815 } 816 817 if (copyin(rblk, krblk, sizeof (rctl_opaque_t)) != 0) { 818 kmem_free(krde, sizeof (rctl_dict_entry_t)); 819 kmem_free(krblk, sizeof (rctl_opaque_t)); 820 kmem_free(kname, MAXPATHLEN); 821 return (set_errno(EFAULT)); 822 } 823 824 rctlsys_rblk_xfrm(krblk, krde, NULL, RBX_FROM_BLK | RBX_CTL); 825 826 if (rctl_global_set(kname, krde) == -1) { 827 kmem_free(krde, sizeof (rctl_dict_entry_t)); 828 kmem_free(krblk, sizeof (rctl_opaque_t)); 829 kmem_free(kname, MAXPATHLEN); 830 return (set_errno(ESRCH)); 831 } 832 833 kmem_free(krde, sizeof (rctl_dict_entry_t)); 834 kmem_free(krblk, sizeof (rctl_opaque_t)); 835 kmem_free(kname, MAXPATHLEN); 836 837 break; 838 default: 839 kmem_free(kname, MAXPATHLEN); 840 return (set_errno(EINVAL)); 841 } 842 843 return (0); 844 } 845 846 long 847 rctlsys(int code, char *name, void *obuf, void *nbuf, size_t obufsz, int flags) 848 { 849 switch (code) { 850 case 0: 851 return (rctlsys_get(name, obuf, nbuf, flags)); 852 853 case 1: 854 return (rctlsys_set(name, obuf, nbuf, flags)); 855 856 case 2: 857 /* 858 * Private call for rctl_walk(3C). 859 */ 860 return (rctlsys_lst(obuf, obufsz)); 861 862 case 3: 863 /* 864 * Private code for rctladm(1M): "rctlctl". 865 */ 866 return (rctlsys_ctl(name, obuf, flags)); 867 868 default: 869 return (set_errno(EINVAL)); 870 } 871 } 872