selinux January 2010 archive
Main Archive Page > Month Archives  > selinux archives
selinux: Re: [RFC PATCH v3] selinux: Fix security_compute_av() t

Re: [RFC PATCH v3] selinux: Fix security_compute_av() to not return unknown class errors when in permissive mode

From: Paul Moore <paul.moore_at_nospam>
Date: Mon Jan 04 2010 - 21:44:46 GMT
To: Stephen Smalley <sds@tycho.nsa.gov>


On Monday 04 January 2010 04:11:37 pm Stephen Smalley wrote:
> 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@gmail.com>
> > Signed-off-by: Paul Moore <paul.moore@hp.com>
>
> 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).

Okay, no problem, you know these bits of code far better than I do.

However, since I seem to keep missing the mark here some very explicit directions on the fix you would like to see would be helpful (I thought I had followed your previous suggestions but evidently I was off) or feel free to cobble something up yourself ... ;)

> > ---
> > 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@tycho.nsa.gov
> > with the words "unsubscribe selinux" without quotes as the message.
>
-- paul moore linux @ hp -- This message was distributed to subscribers of the selinux mailing list. If you no longer wish to subscribe, send mail to majordomo@tycho.nsa.gov with the words "unsubscribe selinux" without quotes as the message.