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.
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.
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]
22 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
28 #include <sys/types.h>
39 #include <nss_dbdefs.h>
44 #include <priv_utils.h>
47 #include <sys/pool_impl.h>
48 #include <sys/rctl_impl.h>
53 for (; *s
!= '\0'; s
++)
58 remove_spaces(char *s
)
65 while (*next
!= '\0') {
66 while (isspace(*next
))
74 build_rctlblk(rctlblk_t
*blk
, int comp_num
, char *component
)
78 uint_t act
= rctlblk_get_local_action(blk
, &sig
);
82 * Setting privilege level for resource control block.
84 xstrtolower(component
);
86 if (strcmp("basic", component
) == 0) {
87 rctlblk_set_privilege(blk
, RCPRIV_BASIC
);
91 if (strcmp("priv", component
) == 0 ||
92 strcmp("privileged", component
) == 0) {
93 rctlblk_set_privilege(blk
, RCPRIV_PRIVILEGED
);
103 * Setting value for resource control block.
105 unsigned long long val
;
108 /* Negative numbers are not allowed */
109 if (strchr(component
, '-') != NULL
)
113 val
= strtoull(component
, &t
, 10);
114 if (errno
!= 0 || t
== component
|| *t
!= '\0')
117 rctlblk_set_value(blk
, (rctl_qty_t
)val
);
122 * Setting one or more actions on this resource control block.
125 if (strcmp("none", component
) == 0) {
126 rctlblk_set_local_action(blk
, 0, 0);
130 if (strcmp("deny", component
) == 0) {
131 act
|= RCTL_LOCAL_DENY
;
133 rctlblk_set_local_action(blk
, act
, sig
);
139 * The last, and trickiest, form of action is the signal
142 if ((signam
= strchr(component
, '=')) == NULL
)
147 if (strcmp("sig", component
) == 0 ||
148 strcmp("signal", component
) == 0) {
149 if (strncmp("SIG", signam
, 3) == 0)
152 if (str2sig(signam
, &sig
) == -1)
155 act
|= RCTL_LOCAL_SIGNAL
;
157 rctlblk_set_local_action(blk
, act
, sig
);
173 #define SETFAILED (-1)
177 #define CLOSEBEFOREOPEN 4
181 reinit_blk(rctlblk_t
*blk
, int local_action
)
183 rctlblk_set_privilege(blk
, RCPRIV_PRIVILEGED
);
184 rctlblk_set_value(blk
, 0);
185 rctlblk_set_local_flags(blk
, 0);
186 rctlblk_set_local_action(blk
, local_action
, 0);
190 rctl_set(char *ctl_name
, char *val
, struct ps_prochandle
*Pr
, int flags
)
193 uint_t component
= 0;
196 char *component_head
;
199 int project_entity
= 0;
204 int teardown_basic
= 0;
205 int teardown_priv
= 0;
207 /* We cannot modify a zone resource control */
208 if (strncmp(ctl_name
, "zone.", strlen("zone.")) == 0) {
214 if (strncmp(ctl_name
, "project.", strlen("project.")) == 0) {
216 } else if ((strncmp(ctl_name
, "process.", strlen("process.")) != 0) &&
217 (strncmp(ctl_name
, "task.", strlen("task.")) != 0)) {
221 /* Determine how many attributes we'll be setting */
222 for (tmp
= val
; *tmp
!= '\0'; tmp
++) {
226 /* Allocate sufficient memory for rctl blocks */
227 if ((count
== 0) || ((ablk
=
228 (rctlblk_t
*)malloc(rctlblk_size() * count
)) == NULL
)) {
234 * In order to set the new rctl's local_action, we'll need the
235 * current value of global_flags. We obtain global_flags by
236 * performing a pr_getrctl().
238 * The ctl_name has been verified as valid, so we have no reason
239 * to suspect that pr_getrctl() will return an error.
241 (void) pr_getrctl(Pr
, ctl_name
, NULL
, blk
, RCTL_FIRST
);
245 * Set initial local action based on global deny properties.
247 rctlblk_set_privilege(blk
, RCPRIV_PRIVILEGED
);
248 rctlblk_set_value(blk
, 0);
249 rctlblk_set_local_flags(blk
, 0);
251 if (rctlblk_get_global_flags(blk
) & RCTL_GLOBAL_DENY_ALWAYS
)
252 local_act
= RCTL_LOCAL_DENY
;
254 local_act
= RCTL_LOCAL_NOACTION
;
256 rctlblk_set_local_action(blk
, local_act
, 0);
262 if (state
& INPAREN
) {
268 component_head
= (char *)val
+ 1;
272 if (state
& INPAREN
) {
278 if (build_rctlblk(blk
, component
,
279 component_head
) == -1) {
287 if (project_entity
&&
288 (rctlblk_get_privilege(blk
) ==
292 if (rctlblk_get_privilege(blk
)
296 if (rctlblk_get_privilege(blk
)
297 == RCPRIV_PRIVILEGED
)
300 if (valuecount
> count
) {
305 if (valuecount
!= count
) {
306 blk
= RCTLBLK_INC(ablk
,
308 /* re-initialize blk */
315 error
= CLOSEBEFOREOPEN
;
319 if (state
& INPAREN
) {
321 if (build_rctlblk(blk
, component
,
322 component_head
) == -1)
326 component_head
= (char *)val
+ 1;
333 else if (state
& INPAREN
)
339 if (!(state
& INPAREN
))
347 /* ablk points to array of rctlblk_t */
352 if (error
!= COMPLETE
) {
357 /* teardown rctls if required */
358 if (!project_entity
) {
360 if ((rnext
= (rctlblk_t
*)malloc(rctlblk_size())) == NULL
) {
366 if (pr_getrctl(Pr
, ctl_name
, NULL
, rnext
, RCTL_FIRST
) == 0) {
368 if ((rctlblk_get_privilege(rnext
) ==
369 RCPRIV_PRIVILEGED
) &&
370 (teardown_priv
== 1)) {
371 (void) pr_setrctl(Pr
, ctl_name
, NULL
,
375 if ((rctlblk_get_privilege(rnext
) ==
376 RCPRIV_BASIC
) && (teardown_basic
== 1)) {
377 (void) pr_setrctl(Pr
, ctl_name
, NULL
,
382 if (pr_getrctl(Pr
, ctl_name
, rnext
, rnext
,
395 if (project_entity
) {
396 if (pr_setprojrctl(Pr
, ctl_name
, blk
, count
, flags
) == -1)
400 while (valuecount
< count
) {
401 if (pr_setrctl(Pr
, ctl_name
,
402 NULL
, blk
, RCTL_INSERT
) == -1) {
407 blk
= RCTLBLK_INC(ablk
, valuecount
);
415 if (error
!= COMPLETE
)
422 rctlwalkfunc(const char *name
, void *data
)
425 if (strcmp(name
, (char *)data
) == 0)
433 * This routine determines if /dev/pool device is present on the system and
434 * pools are currently enabled. We want to do this directly from libproject
435 * without using libpool's pool_get_status() routine because pools could be
436 * completely removed from the system. Return 1 if pools are enabled, or
437 * 0 otherwise. When used inside local zones, always pretend that pools
438 * are disabled because binding is not allowed and we're already in the
444 pool_status_t status
;
447 if (getzoneid() != GLOBAL_ZONEID
)
449 if ((fd
= open("/dev/pool", O_RDONLY
)) < 0)
451 if (ioctl(fd
, POOL_STATUSQ
, &status
) < 0) {
456 return (status
.ps_io_state
);
460 * A pool_name of NULL means to attempt to bind to the default pool.
461 * If the "force" flag is non-zero, the value of "system.bind-default" will be
462 * ignored, and the process will be bound to the default pool if one exists.
465 bind_to_pool(const char *pool_name
, pid_t pid
, int force
)
467 pool_value_t
*pvals
[] = { NULL
, NULL
};
475 if ((conf
= pool_conf_alloc()) == NULL
)
477 if (pool_conf_open(conf
, pool_dynamic_location(), PO_RDONLY
) < 0) {
479 * Pools configuration file is corrupted; allow logins.
481 pool_conf_free(conf
);
484 if (pool_name
!= NULL
&& pool_get_pool(conf
, pool_name
) != NULL
) {
486 * There was a project.pool entry, and the pool it refers to
487 * is a valid (active) pool.
489 (void) pool_conf_close(conf
);
490 pool_conf_free(conf
);
491 if (pool_set_binding(pool_name
, P_PID
, pid
) != PO_SUCCESS
) {
492 if (pool_error() != POE_SYSTEM
)
500 * Bind to the pool with 'pool.default' = 'true' if
501 * 'system.bind-default' = 'true'.
503 if ((pvals
[0] = pool_value_alloc()) == NULL
) {
504 pool_conf_close(conf
);
505 pool_conf_free(conf
);
508 if (!force
&& pool_get_property(conf
, pool_conf_to_elem(conf
),
509 "system.bind-default", pvals
[0]) != POC_BOOL
||
510 pool_value_get_bool(pvals
[0], &bval
) != PO_SUCCESS
||
512 pool_value_free(pvals
[0]);
513 pool_conf_close(conf
);
514 pool_conf_free(conf
);
515 errno
= pool_name
== NULL
? EACCES
: ESRCH
;
518 (void) pool_value_set_name(pvals
[0], "pool.default");
519 pool_value_set_bool(pvals
[0], PO_TRUE
);
520 if ((pools
= pool_query_pools(conf
, &nelem
, pvals
)) == NULL
) {
522 * No default pools exist.
524 pool_value_free(pvals
[0]);
525 pool_conf_close(conf
);
526 pool_conf_free(conf
);
527 errno
= pool_name
== NULL
? EACCES
: ESRCH
;
531 pool_get_property(conf
, pool_to_elem(conf
, pools
[0]), "pool.name",
532 pvals
[0]) != POC_STRING
) {
534 * Configuration is invalid.
537 pool_value_free(pvals
[0]);
538 (void) pool_conf_close(conf
);
539 pool_conf_free(conf
);
543 (void) pool_conf_close(conf
);
544 pool_conf_free(conf
);
545 (void) pool_value_get_string(pvals
[0], &nm
);
546 if (pool_set_binding(nm
, P_PID
, pid
) != PO_SUCCESS
) {
547 if (pool_error() != POE_SYSTEM
)
553 pool_value_free(pvals
[0]);
558 * Changes the assigned project, task and resource pool of a stopped target
561 * We may not have access to the project table if our target process is in
562 * getprojbyname()'s execution path. Similarly, we may not be able to get user
563 * information if the target process is in getpwnam()'s execution path. Thus we
564 * give the caller the option of skipping these checks by providing a pointer to
565 * a pre-validated project structure in proj (whose name matches project_name)
566 * and taking responsibility for ensuring that the target process' owner is a
567 * member of the target project.
569 * Callers of this function should always provide a pre-validated project
570 * structure in proj unless they can be sure that the target process will never
571 * be in setproject_proc()'s execution path.
575 setproject_proc(const char *project_name
, const char *user_name
, int flags
,
576 pid_t pid
, struct ps_prochandle
*Pr
, struct project
*proj
)
578 char pwdbuf
[NSS_BUFLEN_PASSWD
];
579 char prbuf
[PROJECT_BUFSZ
];
586 struct project local_proj
; /* space to store proj if not provided */
587 const char *pool_name
= NULL
;
589 if (project_name
!= NULL
) {
593 if (strcmp(project_name
, "") == 0 ||
596 return (SETPROJ_ERR_TASK
);
600 * If proj is NULL, acquire project information to ensure that
601 * project_name is a valid project, and confirm that user_name
602 * exists and is a member of the specified project.
605 struct passwd
*result
;
606 if ((proj
= getprojbyname(project_name
, &local_proj
,
607 prbuf
, PROJECT_BUFSZ
)) == NULL
) {
609 return (SETPROJ_ERR_TASK
);
612 getpwnam_r(user_name
, &pwd
, pwdbuf
, NSS_BUFLEN_PASSWD
,
616 return (SETPROJ_ERR_TASK
);
619 * Root can join any project.
621 if (pwd
.pw_uid
!= (uid_t
)0 &&
622 !inproj(user_name
, project_name
, prbuf
,
625 return (SETPROJ_ERR_TASK
);
628 projid
= proj
->pj_projid
;
630 projid
= getprojid();
634 if ((kv_array
= _str2kva(proj
->pj_attr
, KV_ASSIGN
,
635 KV_DELIMITER
)) != NULL
) {
636 for (i
= 0; i
< kv_array
->length
; i
++) {
637 if (strcmp(kv_array
->data
[i
].key
,
638 "project.pool") == 0) {
639 pool_name
= kv_array
->data
[i
].value
;
641 if (strcmp(kv_array
->data
[i
].key
, "task.final") == 0) {
648 * Bind process to a pool only if pools are configured
650 if (pools_enabled() == 1) {
653 * Attempt to bind to pool before calling
656 old_pool_name
= pool_get_binding(pid
);
657 if (bind_to_pool(pool_name
, pid
, 0) != 0) {
660 return (SETPROJ_ERR_POOL
);
662 if (pr_settaskid(Pr
, projid
, flags
& TASK_MASK
) == -1) {
663 int saved_errno
= errno
;
668 (void) bind_to_pool(old_pool_name
, pid
, 1);
675 return (SETPROJ_ERR_TASK
);
680 * Pools are not configured, so simply create new task.
682 if (pr_settaskid(Pr
, projid
, flags
& TASK_MASK
) == -1) {
684 return (SETPROJ_ERR_TASK
);
688 if (project_name
== NULL
) {
690 * In the case that we are starting a new task in the
691 * current project, we are finished, since the current
692 * resource controls will still apply. (Implicit behaviour:
693 * a project must be entirely logged out before name
694 * service changes will take effect.)
700 if (kv_array
== NULL
)
703 for (i
= 0; i
< kv_array
->length
; i
++) {
705 * Providing a special, i.e. a non-resource control, key? Then
706 * parse that key here and end with "continue;".
710 * For generic bindings, the kernel performs the binding, as
711 * these are resource controls advertised by kernel subsystems.
715 * Check for known attribute name.
718 if (rctl_walk(rctlwalkfunc
, (void *)kv_array
->data
[i
].key
)
723 return (SETPROJ_ERR_TASK
);
726 ret
= rctl_set(kv_array
->data
[i
].key
,
727 kv_array
->data
[i
].value
, Pr
, flags
& TASK_PROJ_MASK
);
729 if (ret
&& unknown
== 0) {
731 * We only report the first failure.
736 if (ret
&& ret
!= SETFAILED
) {
738 * We abort if we couldn't set a component, but if
739 * it's merely that the system didn't recognize it, we
740 * continue, as this could be a third party attribute.
751 setproject(const char *project_name
, const char *user_name
, int flags
)
753 return (setproject_proc(project_name
, user_name
, flags
, P_MYID
, NULL
,
759 setproject_initpriv(void)
761 static priv_t taskpriv
= PRIV_PROC_TASKID
;
762 static priv_t rctlpriv
= PRIV_SYS_RESOURCE
;
763 static priv_t poolpriv
= PRIV_SYS_RES_CONFIG
;
764 static priv_t schedpriv
= PRIV_PROC_PRIOCNTL
;
769 if (getzoneid() == GLOBAL_ZONEID
) {
770 res
= __init_suid_priv(0, taskpriv
, rctlpriv
, poolpriv
,
773 res
= __init_suid_priv(0, taskpriv
, rctlpriv
, NULL
);
779 nset
= priv_allocset();
782 (void) priv_addset(nset
, taskpriv
);
783 (void) priv_addset(nset
, rctlpriv
);
785 * Only need these if we need to change pools, which can
786 * only happen if the target is in the global zone. Rather
787 * than checking the target's zone just check our own
788 * (since if we're in a non-global zone we won't be able
789 * to control processes in other zones).
791 if (getzoneid() == GLOBAL_ZONEID
) {
792 (void) priv_addset(nset
, poolpriv
);
793 (void) priv_addset(nset
, schedpriv
);