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