xref: /illumos-gate/usr/src/uts/common/syscall/rctlsys.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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