[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]

 



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>
---
 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.

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

  Powered by Linux