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_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
297 bzero(nblk, sizeof (rctl_opaque_t));
298 nblk->rcq_value = usage;
299
300 ret = copyout(nblk, new_rblk, sizeof (rctl_opaque_t));
301 kmem_free(nblk, sizeof (rctl_opaque_t));
302 kmem_free(krde, sizeof (rctl_dict_entry_t));
303 return (ret == 0 ? 0 : set_errno(EFAULT));
304 } else if (action == RCTL_FIRST) {
305
306 mutex_enter(&curproc->p_lock);
307 if (ret = rctl_local_get(hndl, NULL, nval, curproc)) {
308 mutex_exit(&curproc->p_lock);
309 kmem_cache_free(rctl_val_cache, nval);
310 kmem_free(krde, sizeof (rctl_dict_entry_t));
311 return (set_errno(ret));
312 }
313 mutex_exit(&curproc->p_lock);
314 } else {
315 /*
316 * RCTL_NEXT
317 */
318 rctl_val_t *oval;
319 rctl_opaque_t *oblk;
320
321 oblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
322
323 if (copyin(old_rblk, oblk, sizeof (rctl_opaque_t)) == -1) {
324 kmem_cache_free(rctl_val_cache, nval);
325 kmem_free(oblk, sizeof (rctl_opaque_t));
326 kmem_free(krde, sizeof (rctl_dict_entry_t));
327 return (set_errno(EFAULT));
328 }
329
330 oval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
331
332 rctlsys_rblk_xfrm(oblk, NULL, oval, RBX_FROM_BLK | RBX_VAL);
333 mutex_enter(&curproc->p_lock);
334 ret = rctl_local_get(hndl, oval, nval, curproc);
335 mutex_exit(&curproc->p_lock);
336
337 kmem_cache_free(rctl_val_cache, oval);
338 kmem_free(oblk, sizeof (rctl_opaque_t));
339
340 if (ret != 0) {
341 kmem_cache_free(rctl_val_cache, nval);
342 kmem_free(krde, sizeof (rctl_dict_entry_t));
343 return (set_errno(ret));
344 }
345 }
346
347 nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
348
349 rctlsys_rblk_xfrm(nblk, krde, nval, RBX_TO_BLK | RBX_VAL | RBX_CTL);
350
351 kmem_free(krde, sizeof (rctl_dict_entry_t));
352 kmem_cache_free(rctl_val_cache, nval);
353
354 if (copyout(nblk, new_rblk, sizeof (rctl_opaque_t)) == -1) {
355 kmem_free(nblk, sizeof (rctl_opaque_t));
356 return (set_errno(EFAULT));
357 }
358
359 kmem_free(nblk, sizeof (rctl_opaque_t));
360
361 return (0);
362 }
363
364 /*
365 * static long rctlsys_set(char *name, rctl_opaque_t *old_rblk,
366 * rctl_opaque_t *new_rblk, int flags)
367 *
368 * Overview
369 * rctlsys_set() is the implementation of the core login of setrctl(2), which
370 * allows the establishment of resource control values. Flags may take on any
371 * of three exclusive values: RCTL_INSERT, RCTL_DELETE, and RCTL_REPLACE.
372 * RCTL_INSERT ignores old_rblk and inserts the value in the appropriate
373 * position in the ordered sequence of resource control values. RCTL_DELETE
374 * ignores old_rblk and deletes the first resource control value matching
375 * (value, priority) in the given resource block. If no matching value is
376 * found, -1 is returned and errno is set to ENOENT. Finally, in the case of
377 * RCTL_REPLACE, old_rblk is used to match (value, priority); the matching
378 * resource control value in the sequence is replaced with the contents of
379 * new_rblk. Again, if no match is found, -1 is returned and errno is set to
380 * ENOENT.
381 *
382 * rctlsys_set() causes a cursor test, which can reactivate resource controls
383 * that have previously fired.
384 */
385 static long
rctlsys_set(char * name,rctl_opaque_t * old_rblk,rctl_opaque_t * new_rblk,int flags)386 rctlsys_set(char *name, rctl_opaque_t *old_rblk, rctl_opaque_t *new_rblk,
387 int flags)
388 {
389 rctl_val_t *nval;
390 rctl_dict_entry_t *rde;
391 rctl_opaque_t *nblk;
392 rctl_hndl_t hndl;
393 char *kname;
394 size_t klen;
395 long ret = 0;
396 proc_t *pp = NULL;
397 pid_t pid;
398 int action = flags & (~RCTLSYS_ACTION_MASK);
399 rctl_val_t *oval;
400 rctl_val_t *rval1;
401 rctl_val_t *rval2;
402 rctl_val_t *tval;
403 rctl_opaque_t *oblk;
404
405 if (flags & (~RCTLSYS_MASK))
406 return (set_errno(EINVAL));
407
408 if (action != RCTL_INSERT &&
409 action != RCTL_DELETE &&
410 action != RCTL_REPLACE)
411 return (set_errno(EINVAL));
412
413 if (new_rblk == NULL || name == NULL)
414 return (set_errno(EFAULT));
415
416 kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
417 if (copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
418 kmem_free(kname, MAXPATHLEN);
419 return (set_errno(EFAULT));
420 }
421
422 if ((hndl = rctl_hndl_lookup(kname)) == -1) {
423 kmem_free(kname, MAXPATHLEN);
424 return (set_errno(EINVAL));
425 }
426
427 kmem_free(kname, MAXPATHLEN);
428
429 rde = rctl_dict_lookup_hndl(hndl);
430
431 nblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
432
433 if (copyin(new_rblk, nblk, sizeof (rctl_opaque_t)) == -1) {
434 kmem_free(nblk, sizeof (rctl_opaque_t));
435 return (set_errno(EFAULT));
436 }
437
438 nval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
439
440 rctlsys_rblk_xfrm(nblk, NULL, nval, RBX_FROM_BLK | RBX_VAL);
441
442 if (rctl_invalid_value(rde, nval)) {
443 kmem_free(nblk, sizeof (rctl_opaque_t));
444 kmem_cache_free(rctl_val_cache, nval);
445 return (set_errno(EINVAL));
446 }
447
448 /* allocate what we might need before potentially grabbing p_lock */
449 oblk = kmem_alloc(sizeof (rctl_opaque_t), KM_SLEEP);
450 oval = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
451 rval1 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
452 rval2 = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
453
454 if (nval->rcv_privilege == RCPRIV_BASIC) {
455 if (flags & RCTL_USE_RECIPIENT_PID) {
456 pid = nval->rcv_action_recip_pid;
457
458 /* case for manipulating rctl values on other procs */
459 if (pid != curproc->p_pid) {
460 /* cannot be other pid on process rctls */
461 if (rde->rcd_entity == RCENTITY_PROCESS) {
462 ret = set_errno(EINVAL);
463 goto rctlsys_out;
464 }
465 /*
466 * must have privilege to manipulate controls
467 * on other processes
468 */
469 if (secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
470 ret = set_errno(EACCES);
471 goto rctlsys_out;
472 }
473
474 pid = nval->rcv_action_recip_pid;
475 mutex_enter(&pidlock);
476 pp = prfind(pid);
477 if (!pp) {
478 mutex_exit(&pidlock);
479 ret = set_errno(ESRCH);
480 goto rctlsys_out;
481 }
482
483 /*
484 * idle or zombie procs have either not yet
485 * set up their rctls or have already done
486 * their rctl_set_tearoff's.
487 */
488 if (pp->p_stat == SZOMB ||
489 pp->p_stat == SIDL) {
490 mutex_exit(&pidlock);
491 ret = set_errno(ESRCH);
492 goto rctlsys_out;
493 }
494
495 /*
496 * hold this pp's p_lock to ensure that
497 * it does not do it's rctl_set_tearoff
498 * If we did not do this, we could
499 * potentially add rctls to the entity
500 * with a recipient that is a process
501 * that has exited.
502 */
503 mutex_enter(&pp->p_lock);
504 mutex_exit(&pidlock);
505
506 /*
507 * We know that curproc's task, project,
508 * and zone pointers will not change
509 * because functions that change them
510 * call holdlwps(SHOLDFORK1) first.
511 */
512
513 /*
514 * verify that the found pp is in the
515 * current task. If it is, then it
516 * is also within the current project
517 * and zone.
518 */
519 if (rde->rcd_entity == RCENTITY_TASK &&
520 pp->p_task != curproc->p_task) {
521 ret = set_errno(ESRCH);
522 goto rctlsys_out;
523 }
524
525 ASSERT(pp->p_task->tk_proj ==
526 curproc->p_task->tk_proj);
527 ASSERT(pp->p_zone == curproc->p_zone);
528
529
530 nval->rcv_action_recipient = pp;
531 nval->rcv_action_recip_pid = pid;
532
533 } else {
534 /* for manipulating rctl values on this proc */
535 mutex_enter(&curproc->p_lock);
536 pp = curproc;
537 nval->rcv_action_recipient = curproc;
538 nval->rcv_action_recip_pid = curproc->p_pid;
539 }
540
541 } else {
542 /* RCTL_USE_RECIPIENT_PID not set, use this proc */
543 mutex_enter(&curproc->p_lock);
544 pp = curproc;
545 nval->rcv_action_recipient = curproc;
546 nval->rcv_action_recip_pid = curproc->p_pid;
547 }
548
549 } else {
550 /* privileged controls have no recipient pid */
551 mutex_enter(&curproc->p_lock);
552 pp = curproc;
553 nval->rcv_action_recipient = NULL;
554 nval->rcv_action_recip_pid = -1;
555 }
556
557 nval->rcv_firing_time = 0;
558
559 if (action == RCTL_REPLACE) {
560
561 if (copyin(old_rblk, oblk, sizeof (rctl_opaque_t)) == -1) {
562 ret = set_errno(EFAULT);
563 goto rctlsys_out;
564 }
565
566 rctlsys_rblk_xfrm(oblk, NULL, oval, RBX_FROM_BLK | RBX_VAL);
567
568 if (rctl_invalid_value(rde, oval)) {
569 ret = set_errno(EINVAL);
570 goto rctlsys_out;
571 }
572
573 if (oval->rcv_privilege == RCPRIV_BASIC) {
574 if (!(flags & RCTL_USE_RECIPIENT_PID)) {
575 oval->rcv_action_recipient = curproc;
576 oval->rcv_action_recip_pid = curproc->p_pid;
577 }
578 } else {
579 oval->rcv_action_recipient = NULL;
580 oval->rcv_action_recip_pid = -1;
581 }
582
583 /*
584 * Find the real value we're attempting to replace on the
585 * sequence, rather than trusting the one delivered from
586 * userland.
587 */
588 if (ret = rctl_local_get(hndl, NULL, rval1, pp)) {
589 (void) set_errno(ret);
590 goto rctlsys_out;
591 }
592
593 do {
594 if (rval1->rcv_privilege == RCPRIV_SYSTEM ||
595 rctl_val_cmp(oval, rval1, 0) == 0)
596 break;
597
598 tval = rval1;
599 rval1 = rval2;
600 rval2 = tval;
601 } while (rctl_local_get(hndl, rval2, rval1, pp) == 0);
602
603 if (rval1->rcv_privilege == RCPRIV_SYSTEM) {
604 if (rctl_val_cmp(oval, rval1, 1) == 0)
605 ret = set_errno(EPERM);
606 else
607 ret = set_errno(ESRCH);
608
609 goto rctlsys_out;
610 }
611
612 bcopy(rval1, oval, sizeof (rctl_val_t));
613
614 /*
615 * System controls are immutable.
616 */
617 if (nval->rcv_privilege == RCPRIV_SYSTEM) {
618 ret = set_errno(EPERM);
619 goto rctlsys_out;
620 }
621
622 /*
623 * Only privileged processes in the global zone can modify
624 * privileged rctls of type RCENTITY_ZONE; replacing privileged
625 * controls with basic ones are not allowed either. Lowering a
626 * lowerable one might be OK for privileged processes in a
627 * non-global zone, but lowerable rctls probably don't make
628 * sense for zones (hence, not modifiable from within a zone).
629 */
630 if (rde->rcd_entity == RCENTITY_ZONE &&
631 (nval->rcv_privilege == RCPRIV_PRIVILEGED ||
632 oval->rcv_privilege == RCPRIV_PRIVILEGED) &&
633 secpolicy_rctlsys(CRED(), B_TRUE) != 0) {
634 ret = set_errno(EACCES);
635 goto rctlsys_out;
636 }
637
638 /*
639 * Must be privileged to replace a privileged control with
640 * a basic one.
641 */
642 if (oval->rcv_privilege == RCPRIV_PRIVILEGED &&
643 nval->rcv_privilege != RCPRIV_PRIVILEGED &&
644 secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
645 ret = set_errno(EACCES);
646 goto rctlsys_out;
647 }
648
649 /*
650 * Must have lowerable global property for non-privileged
651 * to lower the value of a privileged control; otherwise must
652 * have sufficient privileges to modify privileged controls
653 * at all.
654 */
655 if (oval->rcv_privilege == RCPRIV_PRIVILEGED &&
656 nval->rcv_privilege == RCPRIV_PRIVILEGED &&
657 ((((rde->rcd_flagaction & RCTL_GLOBAL_LOWERABLE) == 0) ||
658 oval->rcv_flagaction != nval->rcv_flagaction ||
659 oval->rcv_action_signal != nval->rcv_action_signal ||
660 oval->rcv_value < nval->rcv_value)) &&
661 secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
662 ret = set_errno(EACCES);
663 goto rctlsys_out;
664 }
665
666 if (ret = rctl_local_replace(hndl, oval, nval, pp)) {
667 (void) set_errno(ret);
668 goto rctlsys_out;
669 }
670
671 /* ensure that nval is not freed */
672 nval = NULL;
673
674 } else if (action == RCTL_INSERT) {
675 /*
676 * System controls are immutable.
677 */
678 if (nval->rcv_privilege == RCPRIV_SYSTEM) {
679 ret = set_errno(EPERM);
680 goto rctlsys_out;
681 }
682
683 /*
684 * Only privileged processes in the global zone may add
685 * privileged zone.* rctls. Only privileged processes
686 * may add other privileged rctls.
687 */
688 if (nval->rcv_privilege == RCPRIV_PRIVILEGED) {
689 if ((rde->rcd_entity == RCENTITY_ZONE &&
690 secpolicy_rctlsys(CRED(), B_TRUE) != 0) ||
691 (rde->rcd_entity != RCENTITY_ZONE &&
692 secpolicy_rctlsys(CRED(), B_FALSE) != 0)) {
693 ret = set_errno(EACCES);
694 goto rctlsys_out;
695 }
696 }
697
698 /*
699 * Only one basic control is allowed per rctl.
700 * If a basic control is being inserted, delete
701 * any other basic control.
702 */
703 if ((nval->rcv_privilege == RCPRIV_BASIC) &&
704 (rctl_local_get(hndl, NULL, rval1, pp) == 0)) {
705 do {
706 if (rval1->rcv_privilege == RCPRIV_BASIC &&
707 rval1->rcv_action_recipient == curproc) {
708 (void) rctl_local_delete(hndl, rval1,
709 pp);
710 if (rctl_local_get(hndl, NULL, rval1,
711 pp) != 0)
712 break;
713 }
714
715 tval = rval1;
716 rval1 = rval2;
717 rval2 = tval;
718 } while (rctl_local_get(hndl, rval2, rval1, pp)
719 == 0);
720 }
721
722
723 if (ret = rctl_local_insert(hndl, nval, pp)) {
724 (void) set_errno(ret);
725 goto rctlsys_out;
726 }
727
728 /* ensure that nval is not freed */
729 nval = NULL;
730
731 } else {
732 /*
733 * RCTL_DELETE
734 */
735 if (nval->rcv_privilege == RCPRIV_SYSTEM) {
736 ret = set_errno(EPERM);
737 goto rctlsys_out;
738 }
739
740 if (nval->rcv_privilege == RCPRIV_PRIVILEGED) {
741 if ((rde->rcd_entity == RCENTITY_ZONE &&
742 secpolicy_rctlsys(CRED(), B_TRUE) != 0) ||
743 (rde->rcd_entity != RCENTITY_ZONE &&
744 secpolicy_rctlsys(CRED(), B_FALSE) != 0)) {
745 ret = set_errno(EACCES);
746 goto rctlsys_out;
747 }
748 }
749
750 if (ret = rctl_local_delete(hndl, nval, pp)) {
751 (void) set_errno(ret);
752 goto rctlsys_out;
753 }
754 }
755
756 rctlsys_out:
757
758 if (pp)
759 mutex_exit(&pp->p_lock);
760
761 kmem_free(nblk, sizeof (rctl_opaque_t));
762 kmem_free(oblk, sizeof (rctl_opaque_t));
763
764 /* only free nval if we did not rctl_local_insert it */
765 if (nval)
766 kmem_cache_free(rctl_val_cache, nval);
767
768 kmem_cache_free(rctl_val_cache, oval);
769 kmem_cache_free(rctl_val_cache, rval1);
770 kmem_cache_free(rctl_val_cache, rval2);
771
772 return (ret);
773 }
774
775 static long
rctlsys_lst(char * ubuf,size_t ubufsz)776 rctlsys_lst(char *ubuf, size_t ubufsz)
777 {
778 char *kbuf;
779 size_t kbufsz;
780
781 kbufsz = rctl_build_name_buf(&kbuf);
782
783 if (kbufsz <= ubufsz &&
784 copyout(kbuf, ubuf, kbufsz) != 0) {
785 kmem_free(kbuf, kbufsz);
786 return (set_errno(EFAULT));
787 }
788
789 kmem_free(kbuf, kbufsz);
790
791 return (kbufsz);
792 }
793
794 static long
rctlsys_ctl(char * name,rctl_opaque_t * rblk,int flags)795 rctlsys_ctl(char *name, rctl_opaque_t *rblk, int flags)
796 {
797 rctl_dict_entry_t *krde;
798 rctl_opaque_t *krblk;
799 char *kname;
800 size_t klen;
801
802 kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
803
804 if (name == NULL || copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
805 kmem_free(kname, MAXPATHLEN);
806 return (set_errno(EFAULT));
807 }
808
809 switch (flags) {
810 case RCTLCTL_GET:
811 krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP);
812 krblk = kmem_zalloc(sizeof (rctl_opaque_t), KM_SLEEP);
813
814 if (rctl_global_get(kname, krde) == -1) {
815 kmem_free(krde, sizeof (rctl_dict_entry_t));
816 kmem_free(krblk, sizeof (rctl_opaque_t));
817 kmem_free(kname, MAXPATHLEN);
818 return (set_errno(ESRCH));
819 }
820
821 rctlsys_rblk_xfrm(krblk, krde, NULL, RBX_TO_BLK | RBX_CTL);
822
823 if (copyout(krblk, rblk, sizeof (rctl_opaque_t)) != 0) {
824 kmem_free(krde, sizeof (rctl_dict_entry_t));
825 kmem_free(krblk, sizeof (rctl_opaque_t));
826 kmem_free(kname, MAXPATHLEN);
827 return (set_errno(EFAULT));
828 }
829
830 kmem_free(krde, sizeof (rctl_dict_entry_t));
831 kmem_free(krblk, sizeof (rctl_opaque_t));
832 kmem_free(kname, MAXPATHLEN);
833 break;
834 case RCTLCTL_SET:
835 if (secpolicy_rctlsys(CRED(), B_TRUE) != 0) {
836 kmem_free(kname, MAXPATHLEN);
837 return (set_errno(EPERM));
838 }
839
840 krde = kmem_alloc(sizeof (rctl_dict_entry_t), KM_SLEEP);
841 krblk = kmem_zalloc(sizeof (rctl_opaque_t), KM_SLEEP);
842
843 if (rctl_global_get(kname, krde) == -1) {
844 kmem_free(krde, sizeof (rctl_dict_entry_t));
845 kmem_free(krblk, sizeof (rctl_opaque_t));
846 kmem_free(kname, MAXPATHLEN);
847 return (set_errno(ESRCH));
848 }
849
850 if (copyin(rblk, krblk, sizeof (rctl_opaque_t)) != 0) {
851 kmem_free(krde, sizeof (rctl_dict_entry_t));
852 kmem_free(krblk, sizeof (rctl_opaque_t));
853 kmem_free(kname, MAXPATHLEN);
854 return (set_errno(EFAULT));
855 }
856
857 rctlsys_rblk_xfrm(krblk, krde, NULL, RBX_FROM_BLK | RBX_CTL);
858
859 if (rctl_global_set(kname, krde) == -1) {
860 kmem_free(krde, sizeof (rctl_dict_entry_t));
861 kmem_free(krblk, sizeof (rctl_opaque_t));
862 kmem_free(kname, MAXPATHLEN);
863 return (set_errno(ESRCH));
864 }
865
866 kmem_free(krde, sizeof (rctl_dict_entry_t));
867 kmem_free(krblk, sizeof (rctl_opaque_t));
868 kmem_free(kname, MAXPATHLEN);
869
870 break;
871 default:
872 kmem_free(kname, MAXPATHLEN);
873 return (set_errno(EINVAL));
874 }
875
876 return (0);
877 }
878
879 /*
880 * The arbitrary maximum number of rctl_opaque_t that we can pass to
881 * rctl_projset().
882 */
883 #define RCTL_PROJSET_MAXSIZE 1024
884
885 static long
rctlsys_projset(char * name,rctl_opaque_t * rblk,size_t size,int flags)886 rctlsys_projset(char *name, rctl_opaque_t *rblk, size_t size, int flags)
887 {
888 rctl_dict_entry_t *krde;
889 rctl_opaque_t *krblk;
890 char *kname;
891 size_t klen;
892 rctl_hndl_t hndl;
893 rctl_val_t *new_values = NULL;
894 rctl_val_t *alloc_values = NULL;
895 rctl_val_t *new_val;
896 rctl_val_t *alloc_val;
897 int error = 0;
898 int count;
899
900 kname = kmem_alloc(MAXPATHLEN, KM_SLEEP);
901
902 if (name == NULL || copyinstr(name, kname, MAXPATHLEN, &klen) != 0) {
903 kmem_free(kname, MAXPATHLEN);
904 return (set_errno(EFAULT));
905 }
906
907 if (size > RCTL_PROJSET_MAXSIZE) {
908 kmem_free(kname, MAXPATHLEN);
909 return (set_errno(EINVAL));
910 }
911
912 if ((hndl = rctl_hndl_lookup(kname)) == -1) {
913 kmem_free(kname, MAXPATHLEN);
914 return (set_errno(EINVAL));
915 }
916
917 krde = rctl_dict_lookup_hndl(hndl);
918
919 /* If not a project entity then exit */
920 if ((krde->rcd_entity != RCENTITY_PROJECT) || (size <= 0)) {
921 kmem_free(kname, MAXPATHLEN);
922 return (set_errno(EINVAL));
923 }
924
925 if (secpolicy_rctlsys(CRED(), B_FALSE) != 0) {
926 kmem_free(kname, MAXPATHLEN);
927 return (set_errno(EPERM));
928 }
929
930 /* Allocate an array large enough for all resource control blocks */
931 krblk = kmem_zalloc(sizeof (rctl_opaque_t) * size, KM_SLEEP);
932
933 if (copyin(rblk, krblk, sizeof (rctl_opaque_t) * size) == 0) {
934
935 for (count = 0; (count < size) && (error == 0); count++) {
936 new_val = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
937 alloc_val = kmem_cache_alloc(rctl_val_cache, KM_SLEEP);
938
939 rctlsys_rblk_xfrm(&krblk[count], NULL, new_val,
940 RBX_FROM_BLK | RBX_VAL);
941
942 /*
943 * Project entity resource control values should always
944 * be privileged
945 */
946 if (new_val->rcv_privilege != RCPRIV_PRIVILEGED) {
947 kmem_cache_free(rctl_val_cache, new_val);
948 kmem_cache_free(rctl_val_cache, alloc_val);
949
950 error = EPERM;
951 } else if (rctl_invalid_value(krde, new_val) == 0) {
952
953 /*
954 * This is a project entity; we do not set
955 * rcv_action_recipient or rcv_action_recip_pid
956 */
957 new_val->rcv_action_recipient = NULL;
958 new_val->rcv_action_recip_pid = -1;
959 new_val->rcv_flagaction |= RCTL_LOCAL_PROJDB;
960 new_val->rcv_firing_time = 0;
961
962 new_val->rcv_prev = NULL;
963 new_val->rcv_next = new_values;
964 new_values = new_val;
965
966 /*
967 * alloc_val is left largely uninitialized, it
968 * is a pre-allocated rctl_val_t which is used
969 * later in rctl_local_replace_all() /
970 * rctl_local_insert_all().
971 */
972 alloc_val->rcv_prev = NULL;
973 alloc_val->rcv_next = alloc_values;
974 alloc_values = alloc_val;
975 } else {
976 kmem_cache_free(rctl_val_cache, new_val);
977 kmem_cache_free(rctl_val_cache, alloc_val);
978
979 error = EINVAL;
980 }
981 }
982
983 } else {
984 error = EFAULT;
985 }
986
987 kmem_free(krblk, sizeof (rctl_opaque_t) * size);
988 kmem_free(kname, MAXPATHLEN);
989
990 if (error) {
991 /*
992 * We will have the same number of items in the alloc_values
993 * linked list, as we have in new_values. However, we remain
994 * cautious, and teardown the linked lists individually.
995 */
996 while (new_values != NULL) {
997 new_val = new_values;
998 new_values = new_values->rcv_next;
999 kmem_cache_free(rctl_val_cache, new_val);
1000 }
1001
1002 while (alloc_values != NULL) {
1003 alloc_val = alloc_values;
1004 alloc_values = alloc_values->rcv_next;
1005 kmem_cache_free(rctl_val_cache, alloc_val);
1006 }
1007
1008 return (set_errno(error));
1009 }
1010
1011 /*
1012 * We take the p_lock here to maintain consistency with other functions
1013 * - rctlsys_get() and rctlsys_set()
1014 */
1015 mutex_enter(&curproc->p_lock);
1016 if (flags & TASK_PROJ_PURGE) {
1017 (void) rctl_local_replace_all(hndl, new_values, alloc_values,
1018 curproc);
1019 } else {
1020 (void) rctl_local_insert_all(hndl, new_values, alloc_values,
1021 curproc);
1022 }
1023 mutex_exit(&curproc->p_lock);
1024
1025 return (0);
1026 }
1027
1028 long
rctlsys(int code,char * name,void * obuf,void * nbuf,size_t obufsz,int flags)1029 rctlsys(int code, char *name, void *obuf, void *nbuf, size_t obufsz, int flags)
1030 {
1031 switch (code) {
1032 case 0:
1033 return (rctlsys_get(name, obuf, nbuf, flags));
1034
1035 case 1:
1036 return (rctlsys_set(name, obuf, nbuf, flags));
1037
1038 case 2:
1039 /*
1040 * Private call for rctl_walk(3C).
1041 */
1042 return (rctlsys_lst(obuf, obufsz));
1043
1044 case 3:
1045 /*
1046 * Private code for rctladm(1M): "rctlctl".
1047 */
1048 return (rctlsys_ctl(name, obuf, flags));
1049 case 4:
1050 /*
1051 * Private code for setproject(3PROJECT).
1052 */
1053 return (rctlsys_projset(name, nbuf, obufsz, flags));
1054
1055 default:
1056 return (set_errno(EINVAL));
1057 }
1058 }
1059