Commit 19439d05 authored by Stephen Smalley's avatar Stephen Smalley Committed by James Morris

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: default avatarAndrew Worsley <amworsley@gmail.com>
Signed-off-by: default avatarStephen D. Smalley <sds@tycho.nsa.gov>
Reviewed-by: default avatarPaul Moore <paul.moore@hp.com>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 8d952504
...@@ -746,9 +746,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, ...@@ -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, ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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,11 +87,10 @@ static u32 latest_granting; ...@@ -87,11 +87,10 @@ 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 {
u16 value; /* policy value */ u16 value; /* policy value */
...@@ -196,23 +195,6 @@ static u16 unmap_class(u16 tclass) ...@@ -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 @@ static void security_dump_masked_av(struct context *scontext, ...@@ -532,7 +514,6 @@ static void security_dump_masked_av(struct context *scontext,
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, ...@@ -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, ...@@ -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, ...@@ -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,11 +585,10 @@ static void type_attribute_bounds_av(struct context *scontext, ...@@ -607,11 +585,10 @@ 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;
struct role_allow *ra; struct role_allow *ra;
...@@ -622,19 +599,14 @@ static int context_struct_compute_av(struct context *scontext, ...@@ -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, ...@@ -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 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) ...@@ -886,110 +856,116 @@ int security_bounded_transition(u32 old_sid, u32 new_sid)
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) { read_lock(&policy_rwlock);
avd->allowed = 0xffffffff; avd_init(avd);
avd->auditallow = 0; if (!ss_initialized)
avd->auditdeny = 0xffffffff; goto allow;
avd->seqno = latest_granting;
return 0; scontext = sidtab_search(&sidtab, ssid);
if (!scontext) {
printk(KERN_ERR "SELinux: %s: unrecognized SID %d\n",
__func__, ssid);
goto out;
} }
read_lock(&policy_rwlock); /* permissive domain? */
rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); 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;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment