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