priv.c revision d93c0b4cf0eaea69e1c297b8812a7474feb926b7
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2010 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* Privilege implementation.
*
* This file provides the infrastructure for privilege sets and limits
* the number of files that requires to include <sys/cred_impl.h> and/or
* <sys/priv_impl.h>.
*
* The Solaris privilege mechanism has been designed in a
* future proof manner. While the kernel may use fixed size arrays
* and fixed bitmasks and bit values, the representation of those
* is kernel private. All external interfaces as well as K-to-K interfaces
* have been constructed in a manner to provide the maximum flexibility.
*
* There can be X privilege sets each containing Y 32 bit words.
* <X, Y> are constant for a kernel invocation.
*
* As a consequence, all privilege set manipulation happens in functions
* below.
*
*/
#include <sys/priv_impl.h>
#include <sys/cred_impl.h>
#include <sys/devpolicy.h>
/*
* Privilege name to number mapping table consists in the generated
* priv_const.c file. This lock protects against updates of the privilege
* names and counts; all other priv_info fields are read-only.
* The actual protected values are:
* global variable nprivs
* the priv_max field
* the priv_names field
*/
/*
* Privilege initialization functions.
*/
void
priv_init(void)
{
#ifdef DEBUG
int alloc_test_priv = 1;
#else
int alloc_test_priv = priv_debug;
#endif
/*
* When booting with priv_debug set or in a DEBUG kernel, then we'll
* add an additional basic privilege and we verify that it is always
* present in E.
*/
if (alloc_test_priv != 0 &&
}
}
/* Utility functions: privilege sets as opaque data types */
/*
* Guts of prgetprivsize.
*/
int
{
return (sizeof (prpriv_t) +
PRIV_SETBYTES - sizeof (priv_chunk_t) +
}
/*
* Guts of prgetpriv.
*/
void
{
int i;
for (i = 0; i < PRIV_NSET; i++)
}
/*
* Guts of pr_spriv:
*
* Set the privileges of a process.
*
* In order to set the privileges, the setting process will need to
* have those privileges in its effective set in order to prevent
* specially privileged processes to easily gain additional privileges.
* Pre-existing privileges can be retained. To change any privileges,
* PRIV_PROC_OWNER needs to be asserted.
*
* In formula:
*
* S' <= S || S' <= S + Ea
*
* the new set must either be subset of the old set or a subset of
* the oldset merged with the effective set of the acting process; or just:
*
* S' <= S + Ea
*
* It's not legal to grow the limit set this way.
*
*/
int
{
int i;
/*
* Set must have proper dimension; infosize must be absent
* or properly sized.
*/
prpriv->pr_infosize < 0)
return (EINVAL);
mutex_exit(&p->p_lock);
mutex_enter(&p->p_lock);
return (EPERM);
}
/* Copy the privilege sets from prpriv to newcred */
/*
* Verify the constraints laid out:
* for the limit set, we require that the new set is a subset
* of the old limit set.
* for all other sets, we require that the new set is either a
* subset of the old set or a subset of the intersection of
* the old limit set and the effective set of the acting process.
*/
for (i = 0; i < PRIV_NSET; i++)
break;
goto err;
/* Load the settable privilege information */
if (prpriv->pr_infosize > 0) {
while (x < lastx) {
switch (pi->priv_info_type) {
case PRIV_INFO_FLAGS:
pii = (priv_info_uint_t *)x;
goto err;
}
break;
default:
goto err;
}
/* Guarantee alignment and forward progress */
goto err;
}
x += pi->priv_info_size;
}
}
/*
* We'll try to copy the privilege aware flag; but since the
* privileges sets are all individually set, they are set
* as if we're privilege aware. If PRIV_AWARE wasn't set
* or was explicitely unset, we need to set the flag and then
* try to get rid of it.
*/
}
mutex_enter(&p->p_crlock);
mutex_exit(&p->p_crlock);
mutex_enter(&p->p_lock);
return (0);
err:
mutex_enter(&p->p_lock);
return (err);
}
*priv_hold_implinfo(void)
{
return (priv_info);
}
void
priv_release_implinfo(void)
{
}
priv_get_implinfo_size(void)
{
return (privinfosize);
}
/*
* Return the nth privilege set
*/
const priv_set_t *
{
switch (set) {
case PRIV_EFFECTIVE:
case PRIV_PERMITTED:
}
}
/*
* Buf must be allocated by caller and contain sufficient space to
* contain all additional info structures using priv_info.priv_infosize.
* The buffer must be properly aligned.
*/
/*ARGSUSED*/
void
{
struct priv_info_uint *ii;
}
int
{
int i;
int wheld = 0;
int len;
char *p;
return (-EINVAL);
name += 5;
for (i = 0; i < nprivs; i++)
return (i);
}
if (!wheld) {
if (!(flag & PRIV_ALLOC)) {
return (-EINVAL);
}
/* check length, validity and available space */
if (len > PRIVNAME_MAX) {
return (-ENAMETOOLONG);
}
for (p = (char *)name; *p != '\0'; p++) {
char c = *p;
if (!((c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '_')) {
return (-EINVAL);
}
}
if (!rw_tryupgrade(&privinfo_lock)) {
wheld = 1;
/* Someone may have added our privilege */
goto rescan;
}
}
return (-ENOMEM);
}
/* make the priv_names[i] and privilege name globally visible */
/* adjust priv count and bytes count */
return (i);
}
/*
* We can't afford locking the privileges here because of the locations
* we call this from; so we make sure that the privileges table
* is visible to us; it is made visible before the value of nprivs is
* updated.
*/
const char *
priv_getbynum(int priv)
{
return (priv_names[priv]);
return (NULL);
}
const char *
priv_getsetbynum(int setno)
{
if (!PRIV_VALIDSET(setno))
return (NULL);
return (priv_setnames[setno]);
}
/*
* Privilege sanity checking when setting: E <= P.
*/
static boolean_t
{
}
/*
* Privilege manipulation functions
*
* Without knowing the details of the privilege set implementation,
* opaque pointers can be used to manipulate sets at will.
*/
void
{
}
void
{
int i;
/* memset? */
for (i = 0; i < PRIV_SETSIZE; i++)
}
void
{
}
void
{
}
{
}
#define PRIV_TEST_BODY(test) \
int i; \
\
for (i = 0; i < PRIV_SETSIZE; i++) \
if (!(test)) \
return (B_FALSE); \
\
return (B_TRUE)
{
}
{
}
{
}
/*
* Return true if a is a subset of b
*/
{
}
#define PRIV_CHANGE_BODY(a, op, b) \
int i; \
\
for (i = 0; i < PRIV_SETSIZE; i++) \
/* B = A ^ B */
void
{
/* CSTYLED */
PRIV_CHANGE_BODY(b, &=, a);
}
/* B = A v B */
void
{
/* CSTYLED */
PRIV_CHANGE_BODY(b, |=, a);
}
/* A = ! A */
void
{
PRIV_CHANGE_BODY(a, = ~, a);
}
/*
* Can the source cred act on the target credential?
*
* We will you allow to gain uids this way but not privileges.
*/
int
{
const priv_set_t *eset;
int idsmatch;
int res = 0;
/* prevent the cred from going away */
goto out;
/*
* Source credential must have the proc_zone privilege if referencing
* a process in another zone.
*/
goto out;
}
goto out;
}
/*
* For writing, the effective set of scr must dominate all sets of tcr,
* We test Pt <= Es (Et <= Pt so no need to test) and It <= Es
* The Limit set of scr must be a superset of the limitset of
* tcr.
*/
out:
else
return (res);
}
/*
* Set the privilege aware bit, adding L to E/P if necessary.
* Each time we set it, we also clear PRIV_AWARE_RESET.
*/
void
{
return;
}
{
/*
* We can clear PA in the following cases:
*
* None of the uids are 0.
* Any uid == 0 and P == L and (Euid != 0 or E == L)
*/
}
/*
* Clear privilege aware bit if it is an idempotent operation and by
* clearing it the process cannot get to uid 0 and all privileges.
*
* This function should be called with caution as it may cause "E" to be
* lost once a processes assumes euid 0 again.
*/
void
{
!priv_can_clear_PA(cr)) {
return;
}
return;
/*
* We now need to adjust P/E in those cases when uids
* are zero; the rules are P' = I & L, E' = I & L;
* but since P = L and E = L, we can use P &= I, E &= I,
* depending on which uids are 0.
*/
}
}
/*
* Reset privilege aware bit if so requested by setting the PRIV_AWARE_RESET
* flag.
*/
void
{
return;
}
/*
* When PRIV_AWARE_RESET is enabled, any change of uids causes
* a change to the P and E sets. Bracketing with
* seteuid(0) ... seteuid(uid)/setreuid(-1, 0) .. setreuid(-1, uid)
* will cause the privilege sets "do the right thing.".
* When the change of the uid is "final", e.g., by using setuid(uid),
* or setreuid(uid, uid) or when the last set*uid() call causes all
* uids to be the same, we set P and E to I & L, like when you exec.
* We make an exception when all the uids are 0; this is required
* when we login as root as in that particular case we cannot
* make a distinction between seteuid(0) and seteuid(uid).
* "finalize" argument that we no longer expect new uid changes,
* cf. setreuid(uid, uid) and setuid(uid).
*/
} else {
}
}
}