On Fri, 2009-12-18 at 13:30 -0500, Paul Moore wrote: > It is possible security_compute_av() to return -EINVAL, even when in > permissive mode, due to unknown object classes and SIDs. This patch fixes > this by doing away with the return value for security_compute_av() and > treating unknown classes and SIDs as permission denials. > > NOTE: This patch has only been compile tested, and is only intended for > review. However, if someone wants to give it a try I would appreciate it > as my time is somewhat limited since I'm technically on "vacation" as of a > few hours ago :) > > Reported-by: Andrew Worsley <amworsley@xxxxxxxxx> > Signed-off-by: Paul Moore <paul.moore@xxxxxx> NAK. Sorry, but I think we need to reconsider this patch, as separating the initialization of the avd from the remainder of the computation inside of context_struct_compute_av is fragile, as is separating the printk error message from the actual error checking (and allow_unknown isn't relevant in that particular case - we have already covered it after we unmap the class). > --- > security/selinux/avc.c | 14 ++-- > security/selinux/include/security.h | 8 +- > security/selinux/selinuxfs.c | 4 + > security/selinux/ss/services.c | 113 +++++++++++++---------------------- > 4 files changed, 54 insertions(+), 85 deletions(-) > > diff --git a/security/selinux/avc.c b/security/selinux/avc.c > index f2dde26..7471a0a 100644 > --- a/security/selinux/avc.c > +++ b/security/selinux/avc.c > @@ -731,7 +731,6 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, > struct avc_node *node; > struct av_decision avd_entry, *avd; > int rc = 0; > - u32 denied; > > BUG_ON(!requested); > > @@ -746,9 +745,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, > else > avd = &avd_entry; > > - rc = security_compute_av(ssid, tsid, tclass, requested, avd); > - if (rc) > - goto out; > + security_compute_av(ssid, tsid, tclass, avd); > rcu_read_lock(); > node = avc_insert(ssid, tsid, tclass, avd); > } else { > @@ -757,12 +754,11 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, > avd = &node->ae.avd; > } > > - denied = requested & ~(avd->allowed); > - > - if (denied) { > + if (requested & ~(avd->allowed)) { > if (flags & AVC_STRICT) > rc = -EACCES; > - else if (!selinux_enforcing || (avd->flags & AVD_FLAGS_PERMISSIVE)) > + else if (!selinux_enforcing || > + (avd->flags & AVD_FLAGS_PERMISSIVE)) > avc_update_node(AVC_CALLBACK_GRANT, requested, ssid, > tsid, tclass, avd->seqno); > else > @@ -770,7 +766,7 @@ int avc_has_perm_noaudit(u32 ssid, u32 tsid, > } > > rcu_read_unlock(); > -out: > + > return rc; > } > > diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h > index 2553266..609334d 100644 > --- a/security/selinux/include/security.h > +++ b/security/selinux/include/security.h > @@ -96,12 +96,10 @@ struct av_decision { > /* definitions of av_decision.flags */ > #define AVD_FLAGS_PERMISSIVE 0x0001 > > -int security_compute_av(u32 ssid, u32 tsid, > - u16 tclass, u32 requested, > - struct av_decision *avd); > +void security_compute_av(u32 ssid, u32 tsid, u16 tclass, > + struct av_decision *avd); > > -int security_compute_av_user(u32 ssid, u32 tsid, > - u16 tclass, u32 requested, > +int security_compute_av_user(u32 ssid, u32 tsid, u16 tclass, > struct av_decision *avd); > > int security_transition_sid(u32 ssid, u32 tsid, > diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c > index fab36fd..a45ac7c 100644 > --- a/security/selinux/selinuxfs.c > +++ b/security/selinux/selinuxfs.c > @@ -511,6 +511,8 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) > if (!tcon) > goto out; > > + /* note: we don't currently use "req" anymore but we'll leave it here > + * just to preserve/enforce the existing userspace API */ > length = -EINVAL; > if (sscanf(buf, "%s %s %hu %x", scon, tcon, &tclass, &req) != 4) > goto out2; > @@ -522,7 +524,7 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) > if (length < 0) > goto out2; > > - length = security_compute_av_user(ssid, tsid, tclass, req, &avd); > + length = security_compute_av_user(ssid, tsid, tclass, &avd); > if (length < 0) > goto out2; > > diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c > index b3efae2..e789b34 100644 > --- a/security/selinux/ss/services.c > +++ b/security/selinux/ss/services.c > @@ -90,7 +90,6 @@ static int context_struct_to_string(struct context *context, char **scontext, > static int context_struct_compute_av(struct context *scontext, > struct context *tcontext, > u16 tclass, > - u32 requested, > struct av_decision *avd); > > struct selinux_mapping { > @@ -196,23 +195,6 @@ static u16 unmap_class(u16 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, > int allow_unknown) > { > @@ -532,7 +514,6 @@ out: > static void type_attribute_bounds_av(struct context *scontext, > struct context *tcontext, > u16 tclass, > - u32 requested, > struct av_decision *avd) > { > struct context lo_scontext; > @@ -553,7 +534,6 @@ static void type_attribute_bounds_av(struct context *scontext, > context_struct_compute_av(&lo_scontext, > tcontext, > tclass, > - requested, > &lo_avd); > if ((lo_avd.allowed & avd->allowed) == avd->allowed) > return; /* no masked permission */ > @@ -569,7 +549,6 @@ static void type_attribute_bounds_av(struct context *scontext, > context_struct_compute_av(scontext, > &lo_tcontext, > tclass, > - requested, > &lo_avd); > if ((lo_avd.allowed & avd->allowed) == avd->allowed) > return; /* no masked permission */ > @@ -586,7 +565,6 @@ static void type_attribute_bounds_av(struct context *scontext, > context_struct_compute_av(&lo_scontext, > &lo_tcontext, > tclass, > - requested, > &lo_avd); > if ((lo_avd.allowed & avd->allowed) == avd->allowed) > return; /* no masked permission */ > @@ -610,7 +588,6 @@ static void type_attribute_bounds_av(struct context *scontext, > static int context_struct_compute_av(struct context *scontext, > struct context *tcontext, > u16 tclass, > - u32 requested, > struct av_decision *avd) > { > struct constraint_node *constraint; > @@ -622,20 +599,8 @@ static int context_struct_compute_av(struct context *scontext, > struct ebitmap_node *snode, *tnode; > unsigned int i, j; > > - /* > - * Initialize the access vectors to the default values. > - */ > - avd->allowed = 0; > - avd->auditallow = 0; > - avd->auditdeny = 0xffffffff; > - avd->seqno = latest_granting; > - avd->flags = 0; > - > - if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) { > - if (printk_ratelimit()) > - printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); > + if (unlikely(!tclass || tclass > policydb.p_classes.nprim)) > return -EINVAL; > - } > > tclass_datum = policydb.class_val_to_struct[tclass - 1]; > > @@ -704,8 +669,7 @@ static int context_struct_compute_av(struct context *scontext, > * constraint, lazy checks have to mask any violated > * permission and notice it to userspace via audit. > */ > - type_attribute_bounds_av(scontext, tcontext, > - tclass, requested, avd); > + type_attribute_bounds_av(scontext, tcontext, tclass, avd); > > return 0; > } > @@ -890,11 +854,11 @@ out: > static int security_compute_av_core(u32 ssid, > u32 tsid, > u16 tclass, > - u32 requested, > + int allow_unknown, > struct av_decision *avd) > { > struct context *scontext = NULL, *tcontext = NULL; > - int rc = 0; > + int rc; > > scontext = sidtab_search(&sidtab, ssid); > if (!scontext) { > @@ -909,8 +873,9 @@ static int security_compute_av_core(u32 ssid, > return -EINVAL; > } > > - rc = context_struct_compute_av(scontext, tcontext, tclass, > - requested, avd); > + rc = context_struct_compute_av(scontext, tcontext, tclass, avd); > + if (rc == -EINVAL && !allow_unknown && printk_ratelimit()) > + printk(KERN_WARNING "SELinux: Invalid class %hu\n", tclass); > > /* permissive domain? */ > if (ebitmap_get_bit(&policydb.permissive_map, scontext->type)) > @@ -924,70 +889,78 @@ static int security_compute_av_core(u32 ssid, > * @ssid: source security identifier > * @tsid: target security identifier > * @tclass: target security class > - * @requested: requested permissions > * @avd: access vector decisions > * > * Compute a set of access vector decisions based on the > * 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, > - u32 tsid, > - u16 orig_tclass, > - u32 orig_requested, > - struct av_decision *avd) > +void security_compute_av(u32 ssid, > + u32 tsid, > + u16 orig_tclass, > + struct av_decision *avd) > { > u16 tclass; > - u32 requested; > int rc; > > read_lock(&policy_rwlock); > > + /* Initialize the access vectors to the default values. */ > + avd->allowed = 0; > + avd->auditallow = 0; > + avd->auditdeny = 0xffffffff; > + avd->seqno = latest_granting; > + avd->flags = 0; > + > if (!ss_initialized) > goto allow; > > - requested = unmap_perm(orig_tclass, orig_requested); > tclass = unmap_class(orig_tclass); > - if (unlikely(orig_tclass && !tclass)) { > - if (policydb.allow_unknown) > - goto allow; > - rc = -EINVAL; > - goto out; > - } > - rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); > + if (unlikely(orig_tclass && !tclass)) > + goto error; > + rc = security_compute_av_core(ssid, tsid, tclass, > + policydb.allow_unknown, avd); > + if (unlikely(rc != 0)) > + goto error; > map_decision(orig_tclass, avd, policydb.allow_unknown); > out: > read_unlock(&policy_rwlock); > - return rc; > + return; > allow: > avd->allowed = 0xffffffff; > + goto out; > +error: > + if (policydb.allow_unknown) > + goto allow; > + avd->allowed = 0; > avd->auditallow = 0; > avd->auditdeny = 0xffffffff; > - avd->seqno = latest_granting; > - avd->flags = 0; > - rc = 0; > goto out; > } > > int security_compute_av_user(u32 ssid, > u32 tsid, > u16 tclass, > - u32 requested, > struct av_decision *avd) > { > int rc; > > + read_lock(&policy_rwlock); > + > + /* Initialize the access vectors to the default values. */ > + avd->allowed = 0; > + avd->auditallow = 0; > + avd->auditdeny = 0xffffffff; > + avd->seqno = latest_granting; > + avd->flags = 0; > + > if (!ss_initialized) { > avd->allowed = 0xffffffff; > - avd->auditallow = 0; > - avd->auditdeny = 0xffffffff; > - avd->seqno = latest_granting; > - return 0; > + rc = 0; > + goto out; > } > > - read_lock(&policy_rwlock); > - rc = security_compute_av_core(ssid, tsid, tclass, requested, avd); > + rc = security_compute_av_core(ssid, tsid, tclass, 0, avd); > +out: > read_unlock(&policy_rwlock); > return rc; > } > > > -- > This message was distributed to subscribers of the selinux mailing list. > If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with > the words "unsubscribe selinux" without quotes as the message. -- Stephen Smalley National Security Agency -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@xxxxxxxxxxxxx with the words "unsubscribe selinux" without quotes as the message.