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 (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright (c) 2018, Joyent, Inc.
24  */
25 
26 
27 #include <sys/task.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 
31 #include <ctype.h>
32 #include <project.h>
33 #include <rctl.h>
34 #include <secdb.h>
35 #include <signal.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <strings.h>
39 #include <nss_dbdefs.h>
40 #include <pwd.h>
41 #include <pool.h>
42 #include <libproc.h>
43 #include <priv.h>
44 #include <priv_utils.h>
45 #include <zone.h>
46 #include <sys/pool.h>
47 #include <sys/pool_impl.h>
48 #include <sys/rctl_impl.h>
49 
50 static void
xstrtolower(char * s)51 xstrtolower(char *s)
52 {
53 	for (; *s != '\0'; s++)
54 		*s = tolower(*s);
55 }
56 
57 static void
remove_spaces(char * s)58 remove_spaces(char *s)
59 {
60 	char *current;
61 	char *next;
62 
63 	current = next = s;
64 
65 	while (*next != '\0') {
66 		while (isspace(*next))
67 			next++;
68 		*current++ = *next++;
69 	}
70 	*current = '\0';
71 }
72 
73 int
build_rctlblk(rctlblk_t * blk,int comp_num,char * component)74 build_rctlblk(rctlblk_t *blk, int comp_num, char *component)
75 {
76 	char *signam;
77 	int sig = 0;
78 	uint_t act = rctlblk_get_local_action(blk, &sig);
79 
80 	if (comp_num == 0) {
81 		/*
82 		 * Setting privilege level for resource control block.
83 		 */
84 		xstrtolower(component);
85 
86 		if (strcmp("basic", component) == 0) {
87 			rctlblk_set_privilege(blk, RCPRIV_BASIC);
88 			return (0);
89 		}
90 
91 		if (strcmp("priv", component) == 0 ||
92 		    strcmp("privileged", component) == 0) {
93 			rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
94 			return (0);
95 		}
96 
97 		return (-1);
98 	}
99 
100 	if (comp_num == 1) {
101 
102 		/*
103 		 * Setting value for resource control block.
104 		 */
105 		unsigned long long val;
106 		char *t;
107 
108 		/* Negative numbers are not allowed */
109 		if (strchr(component, '-') != NULL)
110 			return (-1);
111 
112 		errno = 0;
113 		val = strtoull(component, &t, 10);
114 		if (errno != 0 || t == component || *t != '\0')
115 			return (-1);
116 
117 		rctlblk_set_value(blk, (rctl_qty_t)val);
118 		return (0);
119 	}
120 
121 	/*
122 	 * Setting one or more actions on this resource control block.
123 	 */
124 	if (comp_num >= 2) {
125 		if (strcmp("none", component) == 0) {
126 			rctlblk_set_local_action(blk, 0, 0);
127 			return (0);
128 		}
129 
130 		if (strcmp("deny", component) == 0) {
131 			act |= RCTL_LOCAL_DENY;
132 
133 			rctlblk_set_local_action(blk, act, sig);
134 
135 			return (0);
136 		}
137 
138 		/*
139 		 * The last, and trickiest, form of action is the signal
140 		 * specification.
141 		 */
142 		if ((signam = strchr(component, '=')) == NULL)
143 			return (-1);
144 
145 		*signam++ = '\0';
146 
147 		if (strcmp("sig", component) == 0 ||
148 		    strcmp("signal", component) == 0) {
149 			if (strncmp("SIG", signam, 3) == 0)
150 				signam += 3;
151 
152 			if (str2sig(signam, &sig) == -1)
153 				return (-1);
154 
155 			act |= RCTL_LOCAL_SIGNAL;
156 
157 			rctlblk_set_local_action(blk, act, sig);
158 
159 			return (0);
160 		}
161 	}
162 	return (-1);
163 }
164 
165 /*
166  * States:
167  */
168 #define	INPAREN		0x1
169 
170 /*
171  * Errors:
172  */
173 #define	SETFAILED	(-1)
174 #define	COMPLETE	1
175 #define	NESTING		2
176 #define	UNCLOSED	3
177 #define	CLOSEBEFOREOPEN	4
178 #define	BADSPEC		5
179 
180 static void
reinit_blk(rctlblk_t * blk,int local_action)181 reinit_blk(rctlblk_t *blk, int local_action)
182 {
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);
187 }
188 
189 static int
rctl_set(char * ctl_name,char * val,struct ps_prochandle * Pr,int flags)190 rctl_set(char *ctl_name, char *val, struct ps_prochandle *Pr, int flags)
191 {
192 	int error = 0;
193 	uint_t component = 0;
194 	int valuecount = 0;
195 	uint_t state = 0;
196 	char *component_head;
197 	rctlblk_t *blk;
198 	rctlblk_t *ablk;
199 	int project_entity = 0;
200 	int count = 0;
201 	char *tmp;
202 	int local_act;
203 	rctlblk_t *rnext;
204 	int teardown_basic = 0;
205 	int teardown_priv = 0;
206 
207 	/* We cannot modify a zone resource control */
208 	if (strncmp(ctl_name, "zone.", strlen("zone.")) == 0) {
209 		return (SETFAILED);
210 	}
211 
212 	remove_spaces(val);
213 
214 	if (strncmp(ctl_name, "project.", strlen("project.")) == 0) {
215 		project_entity = 1;
216 	} else if ((strncmp(ctl_name, "process.", strlen("process.")) != 0) &&
217 	    (strncmp(ctl_name, "task.", strlen("task.")) != 0)) {
218 		return (SETFAILED);
219 	}
220 
221 	/* Determine how many attributes we'll be setting */
222 	for (tmp = val; *tmp != '\0'; tmp++) {
223 		if (*tmp == '(')
224 			count++;
225 	}
226 	/* Allocate sufficient memory for rctl blocks */
227 	if ((count == 0) || ((ablk =
228 	    (rctlblk_t *)malloc(rctlblk_size() * count)) == NULL)) {
229 		return (SETFAILED);
230 	}
231 	blk = ablk;
232 
233 	/*
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().
237 	 *
238 	 * The ctl_name has been verified as valid, so we have no reason
239 	 * to suspect that pr_getrctl() will return an error.
240 	 */
241 	(void) pr_getrctl(Pr, ctl_name, NULL, blk, RCTL_FIRST);
242 
243 
244 	/*
245 	 * Set initial local action based on global deny properties.
246 	 */
247 	rctlblk_set_privilege(blk, RCPRIV_PRIVILEGED);
248 	rctlblk_set_value(blk, 0);
249 	rctlblk_set_local_flags(blk, 0);
250 
251 	if (rctlblk_get_global_flags(blk) & RCTL_GLOBAL_DENY_ALWAYS)
252 		local_act = RCTL_LOCAL_DENY;
253 	else
254 		local_act = RCTL_LOCAL_NOACTION;
255 
256 	rctlblk_set_local_action(blk, local_act, 0);
257 
258 	for (; ; val++) {
259 
260 		switch (*val) {
261 			case '(':
262 				if (state & INPAREN) {
263 					error = NESTING;
264 					break;
265 				}
266 
267 				state |= INPAREN;
268 				component_head = (char *)val + 1;
269 
270 				break;
271 			case ')':
272 				if (state & INPAREN) {
273 					*val = '\0';
274 					if (component < 2) {
275 						error = BADSPEC;
276 						break;
277 					}
278 					if (build_rctlblk(blk, component,
279 					    component_head) == -1) {
280 						error = BADSPEC;
281 						break;
282 					}
283 					state &= ~INPAREN;
284 					component = 0;
285 					valuecount++;
286 
287 					if (project_entity &&
288 					    (rctlblk_get_privilege(blk) ==
289 					    RCPRIV_BASIC)) {
290 						error = SETFAILED;
291 					} else {
292 						if (rctlblk_get_privilege(blk)
293 						    == RCPRIV_BASIC)
294 							teardown_basic = 1;
295 
296 						if (rctlblk_get_privilege(blk)
297 						    == RCPRIV_PRIVILEGED)
298 							teardown_priv = 1;
299 
300 						if (valuecount > count) {
301 							free(ablk);
302 							return (SETFAILED);
303 						}
304 
305 						if (valuecount != count) {
306 							blk = RCTLBLK_INC(ablk,
307 							    valuecount);
308 							/* re-initialize blk */
309 							reinit_blk(blk,
310 							    local_act);
311 						}
312 					}
313 
314 				} else {
315 					error = CLOSEBEFOREOPEN;
316 				}
317 				break;
318 			case ',':
319 				if (state & INPAREN) {
320 					*val = '\0';
321 					if (build_rctlblk(blk, component,
322 					    component_head) == -1)
323 						error = BADSPEC;
324 
325 					component++;
326 					component_head = (char *)val + 1;
327 
328 				}
329 				break;
330 			case '\0':
331 				if (valuecount == 0)
332 					error = BADSPEC;
333 				else if (state & INPAREN)
334 					error = UNCLOSED;
335 				else
336 					error = COMPLETE;
337 				break;
338 			default:
339 				if (!(state & INPAREN))
340 					error = BADSPEC;
341 				break;
342 		}
343 
344 		if (error)
345 			break;
346 	}
347 	/* ablk points to array of rctlblk_t */
348 
349 	if (valuecount == 0)
350 		error = BADSPEC;
351 
352 	if (error != COMPLETE) {
353 		free(ablk);
354 		return (error);
355 	}
356 
357 	/* teardown rctls if required */
358 	if (!project_entity) {
359 
360 		if ((rnext = (rctlblk_t *)malloc(rctlblk_size())) == NULL) {
361 			free(ablk);
362 			return (SETFAILED);
363 		}
364 
365 restart:
366 		if (pr_getrctl(Pr, ctl_name, NULL, rnext, RCTL_FIRST) == 0) {
367 			while (1) {
368 				if ((rctlblk_get_privilege(rnext) ==
369 				    RCPRIV_PRIVILEGED) &&
370 				    (teardown_priv == 1)) {
371 					(void) pr_setrctl(Pr, ctl_name, NULL,
372 					    rnext, RCTL_DELETE);
373 					goto restart;
374 				}
375 				if ((rctlblk_get_privilege(rnext) ==
376 				    RCPRIV_BASIC) && (teardown_basic == 1)) {
377 					(void) pr_setrctl(Pr, ctl_name, NULL,
378 					    rnext, RCTL_DELETE);
379 					goto restart;
380 				}
381 
382 				if (pr_getrctl(Pr, ctl_name, rnext, rnext,
383 				    RCTL_NEXT) == -1)
384 					break;
385 			}
386 		}
387 
388 		free(rnext);
389 	}
390 
391 	/* set rctls */
392 
393 	blk = ablk;
394 
395 	if (project_entity) {
396 		if (pr_setprojrctl(Pr, ctl_name, blk, count, flags) == -1)
397 			error = SETFAILED;
398 	} else {
399 		valuecount = 0;
400 		while (valuecount < count) {
401 			if (pr_setrctl(Pr, ctl_name,
402 			    NULL, blk, RCTL_INSERT) == -1) {
403 				error = SETFAILED;
404 				break;
405 				}
406 			valuecount++;
407 			blk = RCTLBLK_INC(ablk, valuecount);
408 		}
409 	}
410 
411 
412 
413 	free(ablk);
414 
415 	if (error != COMPLETE)
416 		return (error);
417 
418 	return (0);
419 }
420 
421 static int
rctlwalkfunc(const char * name,void * data)422 rctlwalkfunc(const char *name, void *data)
423 {
424 
425 	if (strcmp(name, (char *)data) == 0)
426 		return (-1);
427 	else
428 		return (0);
429 
430 }
431 
432 /*
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
439  * right pool.
440  */
441 static int
pools_enabled(void)442 pools_enabled(void)
443 {
444 	pool_status_t status;
445 	int fd;
446 
447 	if (getzoneid() != GLOBAL_ZONEID)
448 		return (0);
449 	if ((fd = open("/dev/pool", O_RDONLY)) < 0)
450 		return (0);
451 	if (ioctl(fd, POOL_STATUSQ, &status) < 0) {
452 		(void) close(fd);
453 		return (0);
454 	}
455 	(void) close(fd);
456 	return (status.ps_io_state);
457 }
458 
459 /*
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.
463  */
464 static int
bind_to_pool(const char * pool_name,pid_t pid,int force)465 bind_to_pool(const char *pool_name, pid_t pid, int force)
466 {
467 	pool_value_t *pvals[] = { NULL, NULL };
468 	pool_t **pools;
469 	uint_t nelem;
470 	uchar_t bval;
471 	pool_conf_t *conf;
472 	const char *nm;
473 	int retval;
474 
475 	if ((conf = pool_conf_alloc()) == NULL)
476 		return (-1);
477 	if (pool_conf_open(conf, pool_dynamic_location(), PO_RDONLY) < 0) {
478 		/*
479 		 * Pools configuration file is corrupted; allow logins.
480 		 */
481 		pool_conf_free(conf);
482 		return (0);
483 	}
484 	if (pool_name != NULL && pool_get_pool(conf, pool_name) != NULL) {
485 		/*
486 		 * There was a project.pool entry, and the pool it refers to
487 		 * is a valid (active) pool.
488 		 */
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)
493 				errno = EINVAL;
494 			return (-1);
495 		}
496 		return (0);
497 	}
498 
499 	/*
500 	 * Bind to the pool with 'pool.default' = 'true' if
501 	 * 'system.bind-default' = 'true'.
502 	 */
503 	if ((pvals[0] = pool_value_alloc()) == NULL) {
504 		(void) pool_conf_close(conf);
505 		pool_conf_free(conf);
506 		return (-1);
507 	}
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 ||
511 	    bval == PO_FALSE) {
512 		pool_value_free(pvals[0]);
513 		(void) pool_conf_close(conf);
514 		pool_conf_free(conf);
515 		errno = pool_name == NULL ? EACCES : ESRCH;
516 		return (-1);
517 	}
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) {
521 		/*
522 		 * No default pools exist.
523 		 */
524 		pool_value_free(pvals[0]);
525 		(void) pool_conf_close(conf);
526 		pool_conf_free(conf);
527 		errno = pool_name == NULL ? EACCES : ESRCH;
528 		return (-1);
529 	}
530 	if (nelem != 1 ||
531 	    pool_get_property(conf, pool_to_elem(conf, pools[0]), "pool.name",
532 	    pvals[0]) != POC_STRING) {
533 		/*
534 		 * Configuration is invalid.
535 		 */
536 		free(pools);
537 		pool_value_free(pvals[0]);
538 		(void) pool_conf_close(conf);
539 		pool_conf_free(conf);
540 		return (0);
541 	}
542 	free(pools);
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)
548 			errno = EINVAL;
549 		retval = -1;
550 	} else {
551 		retval = 0;
552 	}
553 	pool_value_free(pvals[0]);
554 	return (retval);
555 }
556 
557 /*
558  * Changes the assigned project, task and resource pool of a stopped target
559  * process.
560  *
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.
568  *
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.
572  */
573 
574 projid_t
setproject_proc(const char * project_name,const char * user_name,int flags,pid_t pid,struct ps_prochandle * Pr,struct project * proj)575 setproject_proc(const char *project_name, const char *user_name, int flags,
576     pid_t pid, struct ps_prochandle *Pr, struct project *proj)
577 {
578 	char pwdbuf[NSS_BUFLEN_PASSWD];
579 	char prbuf[PROJECT_BUFSZ];
580 	projid_t projid;
581 	struct passwd pwd;
582 	int i;
583 	int unknown = 0;
584 	int ret = 0;
585 	kva_t *kv_array;
586 	struct project local_proj; /* space to store proj if not provided */
587 	const char *pool_name = NULL;
588 
589 	if (project_name != NULL) {
590 		/*
591 		 * Sanity checks.
592 		 */
593 		if (strcmp(project_name, "") == 0 ||
594 		    user_name == NULL) {
595 			errno = EINVAL;
596 			return (SETPROJ_ERR_TASK);
597 		}
598 
599 		/*
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.
603 		 */
604 		if (proj == NULL) {
605 			if ((proj = getprojbyname(project_name, &local_proj,
606 			    prbuf, PROJECT_BUFSZ)) == NULL) {
607 				errno = ESRCH;
608 				return (SETPROJ_ERR_TASK);
609 			}
610 
611 			if (getpwnam_r(user_name, &pwd,
612 			    pwdbuf, NSS_BUFLEN_PASSWD) == NULL) {
613 				errno = ESRCH;
614 				return (SETPROJ_ERR_TASK);
615 			}
616 			/*
617 			 * Root can join any project.
618 			 */
619 			if (pwd.pw_uid != (uid_t)0 &&
620 			    !inproj(user_name, project_name, prbuf,
621 			    PROJECT_BUFSZ)) {
622 				errno = ESRCH;
623 				return (SETPROJ_ERR_TASK);
624 			}
625 		}
626 		projid = proj->pj_projid;
627 	} else {
628 		projid = getprojid();
629 	}
630 
631 
632 	if ((kv_array = _str2kva(proj->pj_attr, KV_ASSIGN,
633 	    KV_DELIMITER)) != NULL) {
634 		for (i = 0; i < kv_array->length; i++) {
635 			if (strcmp(kv_array->data[i].key,
636 			    "project.pool") == 0) {
637 				pool_name = kv_array->data[i].value;
638 			}
639 			if (strcmp(kv_array->data[i].key, "task.final") == 0) {
640 				flags |= TASK_FINAL;
641 			}
642 		}
643 	}
644 
645 	/*
646 	 * Bind process to a pool only if pools are configured
647 	 */
648 	if (pools_enabled() == 1) {
649 		char *old_pool_name;
650 		/*
651 		 * Attempt to bind to pool before calling
652 		 * settaskid().
653 		 */
654 		old_pool_name = pool_get_binding(pid);
655 		if (bind_to_pool(pool_name, pid, 0) != 0) {
656 			if (old_pool_name)
657 				free(old_pool_name);
658 			_kva_free(kv_array);
659 			return (SETPROJ_ERR_POOL);
660 		}
661 		if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
662 			int saved_errno = errno;
663 
664 			/*
665 			 * Undo pool binding.
666 			 */
667 			(void) bind_to_pool(old_pool_name, pid, 1);
668 			if (old_pool_name)
669 				free(old_pool_name);
670 			_kva_free(kv_array);
671 			/*
672 			 * Restore errno
673 			 */
674 			errno = saved_errno;
675 			return (SETPROJ_ERR_TASK);
676 		}
677 		if (old_pool_name)
678 			free(old_pool_name);
679 	} else {
680 		/*
681 		 * Pools are not configured, so simply create new task.
682 		 */
683 		if (pr_settaskid(Pr, projid, flags & TASK_MASK) == -1) {
684 			_kva_free(kv_array);
685 			return (SETPROJ_ERR_TASK);
686 		}
687 	}
688 
689 	if (project_name == NULL) {
690 		/*
691 		 * In the case that we are starting a new task in the
692 		 * current project, we are finished, since the current
693 		 * resource controls will still apply. (Implicit behaviour:
694 		 * a project must be entirely logged out before name
695 		 * service changes will take effect.)
696 		 */
697 		_kva_free(kv_array);
698 		return (projid);
699 	}
700 
701 	if (kv_array == NULL)
702 		return (0);
703 
704 	for (i = 0; i < kv_array->length; i++) {
705 		/*
706 		 * Providing a special, i.e. a non-resource control, key?  Then
707 		 * parse that key here and end with "continue;".
708 		 */
709 
710 		/*
711 		 * For generic bindings, the kernel performs the binding, as
712 		 * these are resource controls advertised by kernel subsystems.
713 		 */
714 
715 		/*
716 		 * Check for known attribute name.
717 		 */
718 		errno = 0;
719 		if (rctl_walk(rctlwalkfunc, (void *)kv_array->data[i].key)
720 		    == 0)
721 			continue;
722 		if (errno) {
723 			_kva_free(kv_array);
724 			return (SETPROJ_ERR_TASK);
725 		}
726 
727 		ret = rctl_set(kv_array->data[i].key,
728 		    kv_array->data[i].value, Pr, flags & TASK_PROJ_MASK);
729 
730 		if (ret && unknown == 0) {
731 			/*
732 			 * We only report the first failure.
733 			 */
734 			unknown = i + 1;
735 		}
736 
737 		if (ret && ret != SETFAILED) {
738 			/*
739 			 * We abort if we couldn't set a component, but if
740 			 * it's merely that the system didn't recognize it, we
741 			 * continue, as this could be a third party attribute.
742 			 */
743 			break;
744 		}
745 	}
746 	_kva_free(kv_array);
747 
748 	return (unknown);
749 }
750 
751 projid_t
setproject(const char * project_name,const char * user_name,int flags)752 setproject(const char *project_name, const char *user_name, int flags)
753 {
754 	return (setproject_proc(project_name, user_name, flags, P_MYID, NULL,
755 	    NULL));
756 }
757 
758 
759 priv_set_t *
setproject_initpriv(void)760 setproject_initpriv(void)
761 {
762 	static priv_t taskpriv = PRIV_PROC_TASKID;
763 	static priv_t rctlpriv = PRIV_SYS_RESOURCE;
764 	static priv_t poolpriv = PRIV_SYS_RES_CONFIG;
765 	static priv_t schedpriv = PRIV_PROC_PRIOCNTL;
766 	int res;
767 
768 	priv_set_t *nset;
769 
770 	if (getzoneid() == GLOBAL_ZONEID) {
771 		res = __init_suid_priv(0, taskpriv, rctlpriv, poolpriv,
772 		    schedpriv, (char *)NULL);
773 	} else {
774 		res = __init_suid_priv(0, taskpriv, rctlpriv, (char *)NULL);
775 	}
776 
777 	if (res != 0)
778 		return (NULL);
779 
780 	nset = priv_allocset();
781 	if (nset != NULL) {
782 		priv_emptyset(nset);
783 		(void) priv_addset(nset, taskpriv);
784 		(void) priv_addset(nset, rctlpriv);
785 		/*
786 		 * Only need these if we need to change pools, which can
787 		 * only happen if the target is in the global zone.  Rather
788 		 * than checking the target's zone just check our own
789 		 * (since if we're in a non-global zone we won't be able
790 		 * to control processes in other zones).
791 		 */
792 		if (getzoneid() == GLOBAL_ZONEID) {
793 			(void) priv_addset(nset, poolpriv);
794 			(void) priv_addset(nset, schedpriv);
795 		}
796 	}
797 	return (nset);
798 }
799