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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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.

[Index of Archives]     [Selinux Refpolicy]     [Linux SGX]     [Fedora Users]     [Fedora Desktop]     [Yosemite Photos]     [Yosemite Camping]     [Yosemite Campsites]     [KDE Users]     [Gnome Users]

  Powered by Linux