xref: /illumos-gate/usr/src/uts/common/os/priv.c (revision 84bf06e9e5fd6d61897cc8c298a0f3e807b27094)
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 /*
27  * Privilege implementation.
28  *
29  * This file provides the infrastructure for privilege sets and limits
30  * the number of files that requires to include <sys/cred_impl.h> and/or
31  * <sys/priv_impl.h>.
32  *
33  * The Solaris privilege mechanism has been designed in a
34  * future proof manner.  While the kernel may use fixed size arrays
35  * and fixed bitmasks and bit values, the representation of those
36  * is kernel private.  All external interfaces as well as K-to-K interfaces
37  * have been constructed in a manner to provide the maximum flexibility.
38  *
39  * There can be X privilege sets each containing Y 32 bit words.
40  * <X, Y> are constant for a kernel invocation.
41  *
42  * As a consequence, all privilege set manipulation happens in functions
43  * below.
44  *
45  */
46 
47 #include <sys/systm.h>
48 #include <sys/ddi.h>
49 #include <sys/kmem.h>
50 #include <sys/sunddi.h>
51 #include <sys/errno.h>
52 #include <sys/debug.h>
53 #include <sys/priv_impl.h>
54 #include <sys/procfs.h>
55 #include <sys/policy.h>
56 #include <sys/cred_impl.h>
57 #include <sys/devpolicy.h>
58 #include <sys/atomic.h>
59 
60 /*
61  * Privilege name to number mapping table consists in the generated
62  * priv_const.c file.  This lock protects against updates of the privilege
63  * names and counts; all other priv_info fields are read-only.
64  * The actual protected values are:
65  *	global variable nprivs
66  *	the priv_max field
67  *	the priv_names field
68  *	the priv names info item (cnt/strings)
69  */
70 krwlock_t privinfo_lock;
71 
72 static boolean_t priv_valid(const cred_t *);
73 
74 priv_set_t priv_fullset;	/* set of all privileges */
75 priv_set_t priv_unsafe;	/* unsafe to exec set-uid root if these are not in L */
76 
77 /*
78  * Privilege initialization functions.
79  * Called from common/os/cred.c when cred_init is called.
80  */
81 
82 void
83 priv_init(void)
84 {
85 	rw_init(&privinfo_lock, NULL, RW_DRIVER, NULL);
86 
87 	PRIV_BASIC_ASSERT(priv_basic);
88 	PRIV_UNSAFE_ASSERT(&priv_unsafe);
89 	priv_fillset(&priv_fullset);
90 
91 	devpolicy_init();
92 }
93 
94 /* Utility functions: privilege sets as opaque data types */
95 
96 /*
97  * Guts of prgetprivsize.
98  */
99 int
100 priv_prgetprivsize(prpriv_t *tmpl)
101 {
102 	return (sizeof (prpriv_t) +
103 	    PRIV_SETBYTES - sizeof (priv_chunk_t) +
104 	    (tmpl ? tmpl->pr_infosize : priv_info->priv_infosize));
105 }
106 
107 /*
108  * Guts of prgetpriv.
109  */
110 void
111 cred2prpriv(const cred_t *cp, prpriv_t *pr)
112 {
113 	priv_set_t *psa;
114 	int i;
115 
116 	pr->pr_nsets = PRIV_NSET;
117 	pr->pr_setsize = PRIV_SETSIZE;
118 	pr->pr_infosize = priv_info->priv_infosize;
119 
120 	psa = (priv_set_t *)pr->pr_sets;
121 
122 	for (i = 0; i < PRIV_NSET; i++)
123 		psa[i] = *priv_getset(cp, i);
124 
125 	priv_getinfo(cp, (char *)pr + PRIV_PRPRIV_INFO_OFFSET(pr));
126 }
127 
128 /*
129  * Guts of pr_spriv:
130  *
131  * Set the privileges of a process.
132  *
133  * In order to set the privileges, the setting process will need to
134  * have those privileges in its effective set in order to prevent
135  * specially privileged processes to easily gain additional privileges.
136  * Pre-existing privileges can be retained.  To change any privileges,
137  * PRIV_PROC_OWNER needs to be asserted.
138  *
139  * In formula:
140  *
141  *	S' <= S || S' <= S + Ea
142  *
143  * the new set must either be subset of the old set or a subset of
144  * the oldset merged with the effective set of the acting process; or just:
145  *
146  *	S' <= S + Ea
147  *
148  * It's not legal to grow the limit set this way.
149  *
150  */
151 int
152 priv_pr_spriv(proc_t *p, prpriv_t *prpriv, const cred_t *cr)
153 {
154 	cred_t *oldcred;
155 	cred_t *newcred;
156 	int i;
157 	int err = EPERM;
158 	cred_priv_t *cp, *ocp;
159 	priv_set_t eset;
160 
161 	ASSERT(MUTEX_HELD(&p->p_lock));
162 
163 	/*
164 	 * Set must have proper dimension; infosize must be absent
165 	 * or properly sized.
166 	 */
167 	if (prpriv->pr_nsets != PRIV_NSET ||
168 	    prpriv->pr_setsize != PRIV_SETSIZE ||
169 	    (prpriv->pr_infosize & (sizeof (uint32_t) - 1)) != 0 ||
170 	    prpriv->pr_infosize > priv_info->priv_infosize ||
171 	    prpriv->pr_infosize < 0)
172 		return (EINVAL);
173 
174 	mutex_exit(&p->p_lock);
175 
176 	if (priv_proc_cred_perm(cr, p, &oldcred, VWRITE) != 0) {
177 		mutex_enter(&p->p_lock);
178 		return (EPERM);
179 	}
180 
181 	newcred = crdup(oldcred);
182 
183 	/* Copy the privilege sets from prpriv to newcred */
184 	bcopy(prpriv->pr_sets, CR_PRIVSETS(newcred), PRIV_SETBYTES);
185 
186 	cp = &newcred->cr_priv;
187 	ocp = &oldcred->cr_priv;
188 	eset = CR_OEPRIV(cr);
189 
190 	priv_intersect(&CR_LPRIV(oldcred), &eset);
191 
192 	/*
193 	 * Verify the constraints laid out:
194 	 * for the limit set, we require that the new set is a subset
195 	 * of the old limit set.
196 	 * for all other sets, we require that the new set is either a
197 	 * subset of the old set or a subset of the intersection of
198 	 * the old limit set and the effective set of the acting process.
199 	 */
200 	for (i = 0; i < PRIV_NSET; i++)
201 		if (!priv_issubset(&cp->crprivs[i], &ocp->crprivs[i]) &&
202 		    (i == PRIV_LIMIT || !priv_issubset(&cp->crprivs[i], &eset)))
203 			break;
204 
205 	crfree(oldcred);
206 
207 	if (i < PRIV_NSET || !priv_valid(newcred))
208 		goto err;
209 
210 	/* Load the settable privilege information */
211 	if (prpriv->pr_infosize > 0) {
212 		char *x = (char *)prpriv + PRIV_PRPRIV_INFO_OFFSET(prpriv);
213 		char *lastx = x + prpriv->pr_infosize;
214 
215 		while (x < lastx) {
216 			priv_info_t *pi = (priv_info_t *)x;
217 			priv_info_uint_t *pii;
218 
219 			switch (pi->priv_info_type) {
220 			case PRIV_INFO_FLAGS:
221 				pii = (priv_info_uint_t *)x;
222 				if (pii->info.priv_info_size != sizeof (*pii)) {
223 					err = EINVAL;
224 					goto err;
225 				}
226 				CR_FLAGS(newcred) &= ~PRIV_USER;
227 				CR_FLAGS(newcred) |= (pii->val & PRIV_USER);
228 				break;
229 			default:
230 				err = EINVAL;
231 				goto err;
232 			}
233 			/* Guarantee alignment and forward progress */
234 			if ((pi->priv_info_size & (sizeof (uint32_t) - 1)) ||
235 			    pi->priv_info_size < sizeof (*pi) ||
236 			    lastx - x > pi->priv_info_size) {
237 				err = EINVAL;
238 				goto err;
239 			}
240 
241 			x += pi->priv_info_size;
242 		}
243 	}
244 
245 	/*
246 	 * We'll try to copy the privilege aware flag; but since the
247 	 * privileges sets are all individually set, they are set
248 	 * as if we're privilege aware.  If PRIV_AWARE wasn't set
249 	 * or was explicitely unset, we need to set the flag and then
250 	 * try to get rid of it.
251 	 */
252 	if ((CR_FLAGS(newcred) & PRIV_AWARE) == 0) {
253 		CR_FLAGS(newcred) |= PRIV_AWARE;
254 		priv_adjust_PA(newcred);
255 	}
256 
257 	mutex_enter(&p->p_crlock);
258 	oldcred = p->p_cred;
259 	p->p_cred = newcred;
260 	mutex_exit(&p->p_crlock);
261 	crfree(oldcred);
262 
263 	mutex_enter(&p->p_lock);
264 	return (0);
265 
266 err:
267 	crfree(newcred);
268 	mutex_enter(&p->p_lock);
269 	return (err);
270 }
271 
272 priv_impl_info_t
273 *priv_hold_implinfo(void)
274 {
275 	rw_enter(&privinfo_lock, RW_READER);
276 	return (priv_info);
277 }
278 
279 void
280 priv_release_implinfo(void)
281 {
282 	rw_exit(&privinfo_lock);
283 }
284 
285 size_t
286 priv_get_implinfo_size(void)
287 {
288 	return (privinfosize);
289 }
290 
291 
292 /*
293  * Return the nth privilege set
294  */
295 const priv_set_t *
296 priv_getset(const cred_t *cr, int set)
297 {
298 	ASSERT(PRIV_VALIDSET(set));
299 
300 	if ((CR_FLAGS(cr) & PRIV_AWARE) == 0)
301 		switch (set) {
302 		case PRIV_EFFECTIVE:
303 			return (&CR_OEPRIV(cr));
304 		case PRIV_PERMITTED:
305 			return (&CR_OPPRIV(cr));
306 		}
307 	return (&CR_PRIVS(cr)->crprivs[set]);
308 }
309 
310 /*
311  * Buf must be allocated by caller and contain sufficient space to
312  * contain all additional info structures using priv_info.priv_infosize.
313  * The buffer must be properly aligned.
314  */
315 /*ARGSUSED*/
316 void
317 priv_getinfo(const cred_t *cr, void *buf)
318 {
319 	struct priv_info_uint *ii;
320 
321 	ii = buf;
322 	ii->val = CR_FLAGS(cr);
323 	ii->info.priv_info_size = (uint32_t)sizeof (*ii);
324 	ii->info.priv_info_type = PRIV_INFO_FLAGS;
325 }
326 
327 int
328 priv_getbyname(const char *name, uint_t flag)
329 {
330 	int i;
331 	int wheld = 0;
332 	int len;
333 	char *p;
334 
335 	if (flag != 0 && flag != PRIV_ALLOC)
336 		return (-EINVAL);
337 
338 	if (strncasecmp(name, "priv_", 5) == 0)
339 		name += 5;
340 
341 	rw_enter(&privinfo_lock, RW_READER);
342 rescan:
343 	for (i = 0; i < nprivs; i++)
344 		if (strcasecmp(priv_names[i], name) == 0) {
345 			rw_exit(&privinfo_lock);
346 			return (i);
347 		}
348 
349 
350 	if (!wheld) {
351 		if (!(flag & PRIV_ALLOC)) {
352 			rw_exit(&privinfo_lock);
353 			return (-EINVAL);
354 		}
355 
356 		/* check length, validity and available space */
357 		len = strlen(name) + 1;
358 
359 		if (len > PRIVNAME_MAX) {
360 			rw_exit(&privinfo_lock);
361 			return (-ENAMETOOLONG);
362 		}
363 
364 		for (p = (char *)name; *p != '\0'; p++) {
365 			char c = *p;
366 
367 			if (!((c >= 'A' && c <= 'Z') ||
368 			    (c >= 'a' && c <= 'z') ||
369 			    (c >= '0' && c <= '9') ||
370 			    c == '_')) {
371 				rw_exit(&privinfo_lock);
372 				return (-EINVAL);
373 			}
374 		}
375 
376 		if (!rw_tryupgrade(&privinfo_lock)) {
377 			rw_exit(&privinfo_lock);
378 			rw_enter(&privinfo_lock, RW_WRITER);
379 			wheld = 1;
380 			/* Someone may have added our privilege */
381 			goto rescan;
382 		}
383 	}
384 
385 	if (nprivs == MAX_PRIVILEGE || len + privbytes > maxprivbytes) {
386 		rw_exit(&privinfo_lock);
387 		return (-ENOMEM);
388 	}
389 
390 	priv_names[i] = p = priv_str + privbytes;
391 
392 	bcopy(name, p, len);
393 
394 	/* make the priv_names[i] and privilege name globally visible */
395 	membar_producer();
396 
397 	/* adjust priv count and bytes count */
398 	priv_ninfo->cnt = priv_info->priv_max = ++nprivs;
399 	privbytes += len;
400 
401 	rw_exit(&privinfo_lock);
402 	return (i);
403 }
404 
405 /*
406  * We can't afford locking the privileges here because of the locations
407  * we call this from; so we make sure that the privileges table
408  * is visible to us; it is made visible before the value of nprivs is
409  * updated.
410  */
411 const char *
412 priv_getbynum(int priv)
413 {
414 	int maxpriv = nprivs;
415 
416 	membar_consumer();
417 
418 	if (priv >= 0 && priv < maxpriv)
419 		return (priv_names[priv]);
420 
421 	return (NULL);
422 }
423 
424 const char *
425 priv_getsetbynum(int setno)
426 {
427 	if (!PRIV_VALIDSET(setno))
428 		return (NULL);
429 
430 	return (priv_setnames[setno]);
431 }
432 
433 /*
434  * Privilege sanity checking when setting: E <= P.
435  */
436 static boolean_t
437 priv_valid(const cred_t *cr)
438 {
439 	return (priv_issubset(&CR_EPRIV(cr), &CR_PPRIV(cr)));
440 }
441 
442 /*
443  * Privilege manipulation functions
444  *
445  * Without knowing the details of the privilege set implementation,
446  * opaque pointers can be used to manipulate sets at will.
447  */
448 void
449 priv_emptyset(priv_set_t *set)
450 {
451 	bzero(set, sizeof (*set));
452 }
453 
454 void
455 priv_fillset(priv_set_t *set)
456 {
457 	int i;
458 
459 	/* memset? */
460 	for (i = 0; i < PRIV_SETSIZE; i++)
461 		set->pbits[i] = ~(priv_chunk_t)0;
462 }
463 
464 void
465 priv_addset(priv_set_t *set, int priv)
466 {
467 	ASSERT(priv >= 0 && priv < MAX_PRIVILEGE);
468 	__PRIV_ASSERT(set, priv);
469 }
470 
471 void
472 priv_delset(priv_set_t *set, int priv)
473 {
474 	ASSERT(priv >= 0 && priv < MAX_PRIVILEGE);
475 	__PRIV_CLEAR(set, priv);
476 }
477 
478 boolean_t
479 priv_ismember(const priv_set_t *set, int priv)
480 {
481 	ASSERT(priv >= 0 && priv < MAX_PRIVILEGE);
482 	return (__PRIV_ISASSERT(set, priv) ? B_TRUE : B_FALSE);
483 }
484 
485 #define	PRIV_TEST_BODY(test) \
486 	int i; \
487 \
488 	for (i = 0; i < PRIV_SETSIZE; i++) \
489 		if (!(test)) \
490 			return (B_FALSE); \
491 \
492 	return (B_TRUE)
493 
494 boolean_t
495 priv_isequalset(const priv_set_t *a, const priv_set_t *b)
496 {
497 	return ((boolean_t)(bcmp(a, b, sizeof (*a)) == 0));
498 }
499 
500 boolean_t
501 priv_isemptyset(const priv_set_t *set)
502 {
503 	PRIV_TEST_BODY(set->pbits[i] == 0);
504 }
505 
506 boolean_t
507 priv_isfullset(const priv_set_t *set)
508 {
509 	PRIV_TEST_BODY(set->pbits[i] == ~(priv_chunk_t)0);
510 }
511 
512 /*
513  * Return true if a is a subset of b
514  */
515 boolean_t
516 priv_issubset(const priv_set_t *a, const priv_set_t *b)
517 {
518 	PRIV_TEST_BODY((a->pbits[i] | b->pbits[i]) == b->pbits[i]);
519 }
520 
521 #define	PRIV_CHANGE_BODY(a, op, b) \
522 	int i; \
523 \
524 	for (i = 0; i < PRIV_SETSIZE; i++) \
525 		a->pbits[i] op b->pbits[i]
526 
527 /* B = A ^ B */
528 void
529 priv_intersect(const priv_set_t *a, priv_set_t *b)
530 {
531 	/* CSTYLED */
532 	PRIV_CHANGE_BODY(b, &=, a);
533 }
534 
535 /* B = A v B */
536 void
537 priv_union(const priv_set_t *a, priv_set_t *b)
538 {
539 	/* CSTYLED */
540 	PRIV_CHANGE_BODY(b, |=, a);
541 }
542 
543 /* A = ! A */
544 void
545 priv_inverse(priv_set_t *a)
546 {
547 	PRIV_CHANGE_BODY(a, = ~, a);
548 }
549 
550 /*
551  * Can the source cred act on the target credential?
552  *
553  * We will you allow to gain uids this way but not privileges.
554  */
555 int
556 priv_proc_cred_perm(const cred_t *scr, proc_t *tp, cred_t **pcr, int mode)
557 {
558 	const priv_set_t *eset;
559 	int idsmatch;
560 	cred_t *tcr;
561 	int res = 0;
562 
563 	/* prevent the cred from going away */
564 	mutex_enter(&tp->p_crlock);
565 	crhold(tcr = tp->p_cred);
566 	mutex_exit(&tp->p_crlock);
567 
568 	if (scr == tcr && !(tp->p_flag & SNOCD))
569 		goto out;
570 
571 	idsmatch = (scr->cr_uid == tcr->cr_uid &&
572 	    scr->cr_uid == tcr->cr_ruid &&
573 	    scr->cr_uid == tcr->cr_suid &&
574 	    scr->cr_gid == tcr->cr_gid &&
575 	    scr->cr_gid == tcr->cr_rgid &&
576 	    scr->cr_gid == tcr->cr_sgid &&
577 	    !(tp->p_flag & SNOCD));
578 
579 	/*
580 	 * Source credential must have the proc_zone privilege if referencing
581 	 * a process in another zone.
582 	 */
583 	if (scr->cr_zone != tcr->cr_zone && secpolicy_proc_zone(scr) != 0) {
584 		res = EACCES;
585 		goto out;
586 	}
587 
588 	if (!(mode & VWRITE)) {
589 		if (!idsmatch && secpolicy_proc_owner(scr, tcr, 0) != 0)
590 			res = EACCES;
591 		goto out;
592 	}
593 
594 	/*
595 	 * For writing, the effective set of scr must dominate all sets of tcr,
596 	 * We test Pt <= Es (Et <= Pt so no need to test) and It <= Es
597 	 * The Limit set of scr must be a superset of the limitset of
598 	 * tcr.
599 	 */
600 	eset = &CR_OEPRIV(scr);
601 
602 	if (!priv_issubset(&CR_IPRIV(tcr), eset) ||
603 	    !priv_issubset(&CR_OPPRIV(tcr), eset) ||
604 	    !priv_issubset(&CR_LPRIV(tcr), &CR_LPRIV(scr)) ||
605 	    !idsmatch && secpolicy_proc_owner(scr, tcr, mode) != 0)
606 		res = EACCES;
607 
608 out:
609 	if (res == 0 && pcr != NULL)
610 		*pcr = tcr;
611 	else
612 		crfree(tcr);
613 	return (res);
614 }
615 
616 /*
617  * Set the privilege aware bit, adding L to E/P if
618  * necessasry.
619  */
620 void
621 priv_set_PA(cred_t *cr)
622 {
623 	ASSERT(cr->cr_ref <= 2);
624 
625 	if (CR_FLAGS(cr) & PRIV_AWARE)
626 		return;
627 
628 	CR_FLAGS(cr) |= PRIV_AWARE;
629 
630 	if (cr->cr_uid == 0)
631 		priv_union(&CR_LPRIV(cr), &CR_EPRIV(cr));
632 
633 	if (cr->cr_uid == 0 || cr->cr_suid == 0 || cr->cr_ruid == 0)
634 		priv_union(&CR_LPRIV(cr), &CR_PPRIV(cr));
635 }
636 
637 boolean_t
638 priv_can_clear_PA(const cred_t *cr)
639 {
640 	/*
641 	 * We can clear PA in the following cases:
642 	 *
643 	 * None of the uids are 0.
644 	 * Any uid == 0 and P == L and (Euid != 0 or E == L)
645 	 */
646 	return ((cr->cr_suid != 0 && cr->cr_ruid != 0 && cr->cr_uid != 0) ||
647 	    priv_isequalset(&CR_PPRIV(cr), &CR_LPRIV(cr)) &&
648 	    (cr->cr_uid != 0 || priv_isequalset(&CR_EPRIV(cr), &CR_LPRIV(cr))));
649 }
650 
651 /*
652  * Clear privilege aware bit if it is an idempotent operation and by
653  * clearing it the process cannot get to uid 0 and all privileges.
654  *
655  * This function should be called with caution as it may cause "E" to be
656  * lost once a processes assumes euid 0 again.
657  */
658 void
659 priv_adjust_PA(cred_t *cr)
660 {
661 	ASSERT(cr->cr_ref <= 2);
662 
663 	if (!(CR_FLAGS(cr) & PRIV_AWARE) ||
664 	    !priv_can_clear_PA(cr))
665 		return;
666 
667 	if (CR_FLAGS(cr) & PRIV_AWARE_INHERIT)
668 		return;
669 
670 	/*
671 	 * We now need to adjust P/E in those cases when uids
672 	 * are zero; the rules are P' = I & L, E' = I & L;
673 	 * but since P = L and E = L, we can use P &= I, E &= I,
674 	 * depending on which uids are 0.
675 	 */
676 	if (cr->cr_suid == 0 || cr->cr_ruid == 0 || cr->cr_uid == 0) {
677 		if (cr->cr_uid == 0)
678 			priv_intersect(&CR_IPRIV(cr), &CR_EPRIV(cr));
679 		priv_intersect(&CR_IPRIV(cr), &CR_PPRIV(cr));
680 	}
681 
682 	CR_FLAGS(cr) &= ~PRIV_AWARE;
683 }
684