rctlsys.c revision 4264:c390df7eb79c
1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28#include <sys/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
60static void
61rctlsys_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 */
144int
145rctl_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 */
218static long
219rctlsys_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 */
351static long
352rctlsys_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
722rctlsys_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
741static long
742rctlsys_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
760static long
761rctlsys_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
851static long
852rctlsys_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		kmem_free(krblk, sizeof (rctl_opaque_t) * size);
950	} else {
951		error = EFAULT;
952	}
953
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
994long
995rctlsys(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