selinux: change the handling of unknown classes
If allow_unknown==deny, SELinux treats an undefined kernel security class as an error condition rather than as a typical permission denial and thus does not allow permissions on undefined classes even when in permissive mode. Change the SELinux logic so that this case is handled as a typical permission denial, subject to the usual permissive mode and permissive domain handling. Also drop the 'requested' argument from security_compute_av() and helpers as it is a legacy of the original security server interface and is unused. Changes: - Handle permissive domains consistently by moving up the test for a permissive domain. - Make security_compute_av_user() consistent with security_compute_av(); the only difference now is that security_compute_av() performs mapping between the kernel-private class and permission indices and the policy values. In the userspace case, this mapping is handled by libselinux. - Moved avd_init inside the policy lock. Based in part on a patch by Paul Moore <paul.moore@hp.com>. Reported-by: Andrew Worsley <amworsley@gmail.com> Signed-off-by: Stephen D. Smalley <sds@tycho.nsa.gov> Reviewed-by: Paul Moore <paul.moore@hp.com> Signed-off-by: James Morris <jmorris@namei.org>
This commit is contained in:
parent
8d9525048c
commit
19439d05b8
@ -746,9 +746,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
|||||||
else
|
else
|
||||||
avd = &avd_entry;
|
avd = &avd_entry;
|
||||||
|
|
||||||
rc = security_compute_av(ssid, tsid, tclass, requested, avd);
|
security_compute_av(ssid, tsid, tclass, avd);
|
||||||
if (rc)
|
|
||||||
goto out;
|
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
node = avc_insert(ssid, tsid, tclass, avd);
|
node = avc_insert(ssid, tsid, tclass, avd);
|
||||||
} else {
|
} else {
|
||||||
@ -770,7 +768,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid,
|
|||||||
}
|
}
|
||||||
|
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
out:
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,13 +96,11 @@ struct av_decision {
|
|||||||
/* definitions of av_decision.flags */
|
/* definitions of av_decision.flags */
|
||||||
#define AVD_FLAGS_PERMISSIVE 0x0001
|
#define AVD_FLAGS_PERMISSIVE 0x0001
|
||||||
|
|
||||||
int security_compute_av(u32 ssid, u32 tsid,
|
void security_compute_av(u32 ssid, u32 tsid,
|
||||||
u16 tclass, u32 requested,
|
u16 tclass, struct av_decision *avd);
|
||||||
struct av_decision *avd);
|
|
||||||
|
|
||||||
int security_compute_av_user(u32 ssid, u32 tsid,
|
void security_compute_av_user(u32 ssid, u32 tsid,
|
||||||
u16 tclass, u32 requested,
|
u16 tclass, struct av_decision *avd);
|
||||||
struct av_decision *avd);
|
|
||||||
|
|
||||||
int security_transition_sid(u32 ssid, u32 tsid,
|
int security_transition_sid(u32 ssid, u32 tsid,
|
||||||
u16 tclass, u32 *out_sid);
|
u16 tclass, u32 *out_sid);
|
||||||
|
@ -494,7 +494,6 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
|
|||||||
char *scon, *tcon;
|
char *scon, *tcon;
|
||||||
u32 ssid, tsid;
|
u32 ssid, tsid;
|
||||||
u16 tclass;
|
u16 tclass;
|
||||||
u32 req;
|
|
||||||
struct av_decision avd;
|
struct av_decision avd;
|
||||||
ssize_t length;
|
ssize_t length;
|
||||||
|
|
||||||
@ -512,7 +511,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
|
|||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
length = -EINVAL;
|
length = -EINVAL;
|
||||||
if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4)
|
if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3)
|
||||||
goto out2;
|
goto out2;
|
||||||
|
|
||||||
length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
|
length = security_context_to_sid(scon, strlen(scon)+1, &ssid);
|
||||||
@ -522,9 +521,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size)
|
|||||||
if (length < 0)
|
if (length < 0)
|
||||||
goto out2;
|
goto out2;
|
||||||
|
|
||||||
length = security_compute_av_user(ssid, tsid, tclass, req, &avd);
|
security_compute_av_user(ssid, tsid, tclass, &avd);
|
||||||
if (length < 0)
|
|
||||||
goto out2;
|
|
||||||
|
|
||||||
length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
|
length = scnprintf(buf, SIMPLE_TRANSACTION_LIMIT,
|
||||||
"%x %x %x %x %u %x",
|
"%x %x %x %x %u %x",
|
||||||
|
@ -87,10 +87,9 @@ static u32 latest_granting;
|
|||||||
static int context_struct_to_string(struct context *context, char **scontext,
|
static int context_struct_to_string(struct context *context, char **scontext,
|
||||||
u32 *scontext_len);
|
u32 *scontext_len);
|
||||||
|
|
||||||
static int context_struct_compute_av(struct context *scontext,
|
static void context_struct_compute_av(struct context *scontext,
|
||||||
struct context *tcontext,
|
struct context *tcontext,
|
||||||
u16 tclass,
|
u16 tclass,
|
||||||
u32 requested,
|
|
||||||
struct av_decision *avd);
|
struct av_decision *avd);
|
||||||
|
|
||||||
struct selinux_mapping {
|
struct selinux_mapping {
|
||||||
@ -196,23 +195,6 @@ static u16 unmap_class(u16 tclass)
|
|||||||
return tclass;
|
return tclass;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 unmap_perm(u16 tclass, u32 tperm)
|
|
||||||
{
|
|
||||||
if (tclass < current_mapping_size) {
|
|
||||||
unsigned i;
|
|
||||||
u32 kperm = 0;
|
|
||||||
|
|
||||||
for (i = 0; i < current_mapping[tclass].num_perms; i++)
|
|
||||||
if (tperm & (1<<i)) {
|
|
||||||
kperm |= current_mapping[tclass].perms[i];
|
|
||||||
tperm &= ~(1<<i);
|
|
||||||
}
|
|
||||||
return kperm;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tperm;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void map_decision(u16 tclass, struct av_decision *avd,
|
static void map_decision(u16 tclass, struct av_decision *avd,
|
||||||
int allow_unknown)
|
int allow_unknown)
|
||||||
{
|
{
|
||||||
@ -532,7 +514,6 @@ out:
|
|||||||
static void type_attribute_bounds_av(struct context *scontext,
|
static void type_attribute_bounds_av(struct context *scontext,
|
||||||
struct context *tcontext,
|
struct context *tcontext,
|
||||||
u16 tclass,
|
u16 tclass,
|
||||||
u32 requested,
|
|
||||||
struct av_decision *avd)
|
struct av_decision *avd)
|
||||||
{
|
{
|
||||||
struct context lo_scontext;
|
struct context lo_scontext;
|
||||||
@ -553,7 +534,6 @@ static void type_attribute_bounds_av(struct context *scontext,
|
|||||||
context_struct_compute_av(&lo_scontext,
|
context_struct_compute_av(&lo_scontext,
|
||||||
tcontext,
|
tcontext,
|
||||||
tclass,
|
tclass,
|
||||||
requested,
|
|
||||||
&lo_avd);
|
&lo_avd);
|
||||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||||
return; /* no masked permission */
|
return; /* no masked permission */
|
||||||
@ -569,7 +549,6 @@ static void type_attribute_bounds_av(struct context *scontext,
|
|||||||
context_struct_compute_av(scontext,
|
context_struct_compute_av(scontext,
|
||||||
&lo_tcontext,
|
&lo_tcontext,
|
||||||
tclass,
|
tclass,
|
||||||
requested,
|
|
||||||
&lo_avd);
|
&lo_avd);
|
||||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||||
return; /* no masked permission */
|
return; /* no masked permission */
|
||||||
@ -586,7 +565,6 @@ static void type_attribute_bounds_av(struct context *scontext,
|
|||||||
context_struct_compute_av(&lo_scontext,
|
context_struct_compute_av(&lo_scontext,
|
||||||
&lo_tcontext,
|
&lo_tcontext,
|
||||||
tclass,
|
tclass,
|
||||||
requested,
|
|
||||||
&lo_avd);
|
&lo_avd);
|
||||||
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
if ((lo_avd.allowed & avd->allowed) == avd->allowed)
|
||||||
return; /* no masked permission */
|
return; /* no masked permission */
|
||||||
@ -607,10 +585,9 @@ static void type_attribute_bounds_av(struct context *scontext,
|
|||||||
* Compute access vectors based on a context structure pair for
|
* Compute access vectors based on a context structure pair for
|
||||||
* the permissions in a particular class.
|
* the permissions in a particular class.
|
||||||
*/
|
*/
|
||||||
static int context_struct_compute_av(struct context *scontext,
|
static void context_struct_compute_av(struct context *scontext,
|
||||||
struct context *tcontext,
|
struct context *tcontext,
|
||||||
u16 tclass,
|
u16 tclass,
|
||||||
u32 requested,
|
|
||||||
struct av_decision *avd)
|
struct av_decision *avd)
|
||||||
{
|
{
|
||||||
struct constraint_node *constraint;
|
struct constraint_node *constraint;
|
||||||
@ -622,19 +599,14 @@ static int context_struct_compute_av(struct context *scontext,
|
|||||||
struct ebitmap_node *snode, *tnode;
|
struct ebitmap_node *snode, *tnode;
|
||||||
unsigned int i, j;
|
unsigned int i, j;
|
||||||
|
|
||||||
/*
|
|
||||||
* Initialize the access vectors to the default values.
|
|
||||||
*/
|
|
||||||
avd->allowed = 0;
|
avd->allowed = 0;
|
||||||
avd->auditallow = 0;
|
avd->auditallow = 0;
|
||||||
avd->auditdeny = 0xffffffff;
|
avd->auditdeny = 0xffffffff;
|
||||||
avd->seqno = latest_granting;
|
|
||||||
avd->flags = 0;
|
|
||||||
|
|
||||||
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
|
if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) {
|
||||||
if (printk_ratelimit())
|
if (printk_ratelimit())
|
||||||
printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass);
|
printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass);
|
||||||
return -EINVAL;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
tclass_datum = policydb.class_val_to_struct[tclass - 1];
|
tclass_datum = policydb.class_val_to_struct[tclass - 1];
|
||||||
@ -705,9 +677,7 @@ static int context_struct_compute_av(struct context *scontext,
|
|||||||
* permission and notice it to userspace via audit.
|
* permission and notice it to userspace via audit.
|
||||||
*/
|
*/
|
||||||
type_attribute_bounds_av(scontext, tcontext,
|
type_attribute_bounds_av(scontext, tcontext,
|
||||||
tclass, requested, avd);
|
tclass, avd);
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int security_validtrans_handle_fail(struct context *ocontext,
|
static int security_validtrans_handle_fail(struct context *ocontext,
|
||||||
@ -886,110 +856,116 @@ out:
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void avd_init(struct av_decision *avd)
|
||||||
static int security_compute_av_core(u32 ssid,
|
|
||||||
u32 tsid,
|
|
||||||
u16 tclass,
|
|
||||||
u32 requested,
|
|
||||||
struct av_decision *avd)
|
|
||||||
{
|
{
|
||||||
struct context *scontext = NULL, *tcontext = NULL;
|
avd->allowed = 0;
|
||||||
int rc = 0;
|
avd->auditallow = 0;
|
||||||
|
avd->auditdeny = 0xffffffff;
|
||||||
scontext = sidtab_search(&sidtab, ssid);
|
avd->seqno = latest_granting;
|
||||||
if (!scontext) {
|
avd->flags = 0;
|
||||||
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
|
||||||
__func__, ssid);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
tcontext = sidtab_search(&sidtab, tsid);
|
|
||||||
if (!tcontext) {
|
|
||||||
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
|
||||||
__func__, tsid);
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = context_struct_compute_av(scontext, tcontext, tclass,
|
|
||||||
requested, avd);
|
|
||||||
|
|
||||||
/* permissive domain? */
|
|
||||||
if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
|
|
||||||
avd->flags |= AVD_FLAGS_PERMISSIVE;
|
|
||||||
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* security_compute_av - Compute access vector decisions.
|
* security_compute_av - Compute access vector decisions.
|
||||||
* @ssid: source security identifier
|
* @ssid: source security identifier
|
||||||
* @tsid: target security identifier
|
* @tsid: target security identifier
|
||||||
* @tclass: target security class
|
* @tclass: target security class
|
||||||
* @requested: requested permissions
|
|
||||||
* @avd: access vector decisions
|
* @avd: access vector decisions
|
||||||
*
|
*
|
||||||
* Compute a set of access vector decisions based on the
|
* Compute a set of access vector decisions based on the
|
||||||
* SID pair (@ssid, @tsid) for the permissions in @tclass.
|
* SID pair (@ssid, @tsid) for the permissions in @tclass.
|
||||||
* Return -%EINVAL if any of the parameters are invalid or %0
|
|
||||||
* if the access vector decisions were computed successfully.
|
|
||||||
*/
|
*/
|
||||||
int security_compute_av(u32 ssid,
|
void security_compute_av(u32 ssid,
|
||||||
u32 tsid,
|
u32 tsid,
|
||||||
u16 orig_tclass,
|
u16 orig_tclass,
|
||||||
u32 orig_requested,
|
|
||||||
struct av_decision *avd)
|
struct av_decision *avd)
|
||||||
{
|
{
|
||||||
u16 tclass;
|
u16 tclass;
|
||||||
u32 requested;
|
struct context *scontext = NULL, *tcontext = NULL;
|
||||||
int rc;
|
|
||||||
|
|
||||||
read_lock(&policy_rwlock);
|
read_lock(&policy_rwlock);
|
||||||
|
avd_init(avd);
|
||||||
if (!ss_initialized)
|
if (!ss_initialized)
|
||||||
goto allow;
|
goto allow;
|
||||||
|
|
||||||
requested = unmap_perm(orig_tclass, orig_requested);
|
scontext = sidtab_search(&sidtab, ssid);
|
||||||
|
if (!scontext) {
|
||||||
|
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||||
|
__func__, ssid);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* permissive domain? */
|
||||||
|
if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
|
||||||
|
avd->flags |= AVD_FLAGS_PERMISSIVE;
|
||||||
|
|
||||||
|
tcontext = sidtab_search(&sidtab, tsid);
|
||||||
|
if (!tcontext) {
|
||||||
|
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||||
|
__func__, tsid);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
tclass = unmap_class(orig_tclass);
|
tclass = unmap_class(orig_tclass);
|
||||||
if (unlikely(orig_tclass && !tclass)) {
|
if (unlikely(orig_tclass && !tclass)) {
|
||||||
if (policydb.allow_unknown)
|
if (policydb.allow_unknown)
|
||||||
goto allow;
|
goto allow;
|
||||||
rc = -EINVAL;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
|
context_struct_compute_av(scontext, tcontext, tclass, avd);
|
||||||
map_decision(orig_tclass, avd, policydb.allow_unknown);
|
map_decision(orig_tclass, avd, policydb.allow_unknown);
|
||||||
out:
|
out:
|
||||||
read_unlock(&policy_rwlock);
|
read_unlock(&policy_rwlock);
|
||||||
return rc;
|
return;
|
||||||
allow:
|
allow:
|
||||||
avd->allowed = 0xffffffff;
|
avd->allowed = 0xffffffff;
|
||||||
avd->auditallow = 0;
|
|
||||||
avd->auditdeny = 0xffffffff;
|
|
||||||
avd->seqno = latest_granting;
|
|
||||||
avd->flags = 0;
|
|
||||||
rc = 0;
|
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
int security_compute_av_user(u32 ssid,
|
void security_compute_av_user(u32 ssid,
|
||||||
u32 tsid,
|
u32 tsid,
|
||||||
u16 tclass,
|
u16 tclass,
|
||||||
u32 requested,
|
|
||||||
struct av_decision *avd)
|
struct av_decision *avd)
|
||||||
{
|
{
|
||||||
int rc;
|
struct context *scontext = NULL, *tcontext = NULL;
|
||||||
|
|
||||||
if (!ss_initialized) {
|
|
||||||
avd->allowed = 0xffffffff;
|
|
||||||
avd->auditallow = 0;
|
|
||||||
avd->auditdeny = 0xffffffff;
|
|
||||||
avd->seqno = latest_granting;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
read_lock(&policy_rwlock);
|
read_lock(&policy_rwlock);
|
||||||
rc = security_compute_av_core(ssid, tsid, tclass, requested, avd);
|
avd_init(avd);
|
||||||
|
if (!ss_initialized)
|
||||||
|
goto allow;
|
||||||
|
|
||||||
|
scontext = sidtab_search(&sidtab, ssid);
|
||||||
|
if (!scontext) {
|
||||||
|
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||||
|
__func__, ssid);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* permissive domain? */
|
||||||
|
if (ebitmap_get_bit(&policydb.permissive_map, scontext->type))
|
||||||
|
avd->flags |= AVD_FLAGS_PERMISSIVE;
|
||||||
|
|
||||||
|
tcontext = sidtab_search(&sidtab, tsid);
|
||||||
|
if (!tcontext) {
|
||||||
|
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
|
||||||
|
__func__, tsid);
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(!tclass)) {
|
||||||
|
if (policydb.allow_unknown)
|
||||||
|
goto allow;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
context_struct_compute_av(scontext, tcontext, tclass, avd);
|
||||||
|
out:
|
||||||
read_unlock(&policy_rwlock);
|
read_unlock(&policy_rwlock);
|
||||||
return rc;
|
return;
|
||||||
|
allow:
|
||||||
|
avd->allowed = 0xffffffff;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user