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