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) 2012, Joyent, Inc. All rights reserved.
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 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 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 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