ec226c2
From 7ea59202db8d20806d9ae552acd1875c3a978bcc Mon Sep 17 00:00:00 2001
ec226c2
From: Stephen Smalley <sds@tycho.nsa.gov>
ec226c2
Date: Mon, 23 May 2016 10:54:11 -0400
ec226c2
Subject: [PATCH] selinux: Only apply bounds checking to source types
ec226c2
ec226c2
The current bounds checking of both source and target types
ec226c2
requires allowing any domain that has access to the child
ec226c2
domain to also have the same permissions to the parent, which
ec226c2
is undesirable.  Drop the target bounds checking.
ec226c2
ec226c2
KaiGai Kohei originally removed all use of target bounds in
ec226c2
commit 7d52a155e38d ("selinux: remove dead code in
ec226c2
type_attribute_bounds_av()") but this was reverted in
ec226c2
commit 2ae3ba39389b ("selinux: libsepol: remove dead code in
ec226c2
check_avtab_hierarchy_callback()") because it would have
ec226c2
required explicitly allowing the parent any permissions
ec226c2
to the child that the child is allowed to itself.
ec226c2
ec226c2
This change in contrast retains the logic for the case where both
ec226c2
source and target types are bounded, thereby allowing access
ec226c2
if the parent of the source is allowed the corresponding
ec226c2
permissions to the parent of the target.  Further, this change
ec226c2
reworks the logic such that we only perform a single computation
ec226c2
for each case and there is no ambiguity as to how to resolve
ec226c2
a bounds violation.
ec226c2
ec226c2
Under the new logic, if the source type and target types are both
ec226c2
bounded, then the parent of the source type must be allowed the same
ec226c2
permissions to the parent of the target type.  If only the source
ec226c2
type is bounded, then the parent of the source type must be allowed
ec226c2
the same permissions to the target type.
ec226c2
ec226c2
Examples of the new logic and comparisons with the old logic:
ec226c2
1. If we have:
ec226c2
	typebounds A B;
ec226c2
then:
ec226c2
	allow B self:process <permissions>;
ec226c2
will satisfy the bounds constraint iff:
ec226c2
	allow A self:process <permissions>;
ec226c2
is also allowed in policy.
ec226c2
ec226c2
Under the old logic, the allow rule on B satisfies the
ec226c2
bounds constraint if any of the following three are allowed:
ec226c2
	allow A B:process <permissions>; or
ec226c2
	allow B A:process <permissions>; or
ec226c2
	allow A self:process <permissions>;
ec226c2
However, either of the first two ultimately require the third to
ec226c2
satisfy the bounds constraint under the old logic, and therefore
ec226c2
this degenerates to the same result (but is more efficient - we only
ec226c2
need to perform one compute_av call).
ec226c2
ec226c2
2. If we have:
ec226c2
	typebounds A B;
ec226c2
	typebounds A_exec B_exec;
ec226c2
then:
ec226c2
	allow B B_exec:file <permissions>;
ec226c2
will satisfy the bounds constraint iff:
ec226c2
	allow A A_exec:file <permissions>;
ec226c2
is also allowed in policy.
ec226c2
ec226c2
This is essentially the same as #1; it is merely included as
ec226c2
an example of dealing with object types related to a bounded domain
ec226c2
in a manner that satisfies the bounds relationship.  Note that
ec226c2
this approach is preferable to leaving B_exec unbounded and having:
ec226c2
	allow A B_exec:file <permissions>;
ec226c2
in policy because that would allow B's entrypoints to be used to
ec226c2
enter A.  Similarly for _tmp or other related types.
ec226c2
ec226c2
3. If we have:
ec226c2
	typebounds A B;
ec226c2
and an unbounded type T, then:
ec226c2
	allow B T:file <permissions>;
ec226c2
will satisfy the bounds constraint iff:
ec226c2
	allow A T:file <permissions>;
ec226c2
is allowed in policy.
ec226c2
ec226c2
The old logic would have been identical for this example.
ec226c2
ec226c2
4. If we have:
ec226c2
	typebounds A B;
ec226c2
and an unbounded domain D, then:
ec226c2
	allow D B:unix_stream_socket <permissions>;
ec226c2
is not subject to any bounds constraints under the new logic
ec226c2
because D is not bounded.  This is desirable so that we can
ec226c2
allow a domain to e.g. connectto a child domain without having
ec226c2
to allow it to do the same to its parent.
ec226c2
ec226c2
The old logic would have required:
ec226c2
	allow D A:unix_stream_socket <permissions>;
ec226c2
to also be allowed in policy.
ec226c2
ec226c2
Signed-off-by: Stephen Smalley <sds@tycho.nsa.gov>
ec226c2
[PM: re-wrapped description to appease checkpatch.pl]
ec226c2
Signed-off-by: Paul Moore <paul@paul-moore.com>
ec226c2
---
ec226c2
 security/selinux/ss/services.c | 70 +++++++++++++-----------------------------
ec226c2
 1 file changed, 22 insertions(+), 48 deletions(-)
ec226c2
ec226c2
diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c
ec226c2
index 89df646..082b20c 100644
ec226c2
--- a/security/selinux/ss/services.c
ec226c2
+++ b/security/selinux/ss/services.c
ec226c2
@@ -543,7 +543,7 @@ static void type_attribute_bounds_av(struct context *scontext,
ec226c2
 				     struct av_decision *avd)
ec226c2
 {
ec226c2
 	struct context lo_scontext;
ec226c2
-	struct context lo_tcontext;
ec226c2
+	struct context lo_tcontext, *tcontextp = tcontext;
ec226c2
 	struct av_decision lo_avd;
ec226c2
 	struct type_datum *source;
ec226c2
 	struct type_datum *target;
ec226c2
@@ -553,67 +553,41 @@ static void type_attribute_bounds_av(struct context *scontext,
ec226c2
 				    scontext->type - 1);
ec226c2
 	BUG_ON(!source);
ec226c2
 
ec226c2
+	if (!source->bounds)
ec226c2
+		return;
ec226c2
+
ec226c2
 	target = flex_array_get_ptr(policydb.type_val_to_struct_array,
ec226c2
 				    tcontext->type - 1);
ec226c2
 	BUG_ON(!target);
ec226c2
 
ec226c2
-	if (source->bounds) {
ec226c2
-		memset(&lo_avd, 0, sizeof(lo_avd));
ec226c2
-
ec226c2
-		memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
ec226c2
-		lo_scontext.type = source->bounds;
ec226c2
+	memset(&lo_avd, 0, sizeof(lo_avd));
ec226c2
 
ec226c2
-		context_struct_compute_av(&lo_scontext,
ec226c2
-					  tcontext,
ec226c2
-					  tclass,
ec226c2
-					  &lo_avd,
ec226c2
-					  NULL);
ec226c2
-		if ((lo_avd.allowed & avd->allowed) == avd->allowed)
ec226c2
-			return;		/* no masked permission */
ec226c2
-		masked = ~lo_avd.allowed & avd->allowed;
ec226c2
-	}
ec226c2
+	memcpy(&lo_scontext, scontext, sizeof(lo_scontext));
ec226c2
+	lo_scontext.type = source->bounds;
ec226c2
 
ec226c2
 	if (target->bounds) {
ec226c2
-		memset(&lo_avd, 0, sizeof(lo_avd));
ec226c2
-
ec226c2
 		memcpy(&lo_tcontext, tcontext, sizeof(lo_tcontext));
ec226c2
 		lo_tcontext.type = target->bounds;
ec226c2
-
ec226c2
-		context_struct_compute_av(scontext,
ec226c2
-					  &lo_tcontext,
ec226c2
-					  tclass,
ec226c2
-					  &lo_avd,
ec226c2
-					  NULL);
ec226c2
-		if ((lo_avd.allowed & avd->allowed) == avd->allowed)
ec226c2
-			return;		/* no masked permission */
ec226c2
-		masked = ~lo_avd.allowed & avd->allowed;
ec226c2
+		tcontextp = &lo_tcontext;
ec226c2
 	}
ec226c2
 
ec226c2
-	if (source->bounds && target->bounds) {
ec226c2
-		memset(&lo_avd, 0, sizeof(lo_avd));
ec226c2
-		/*
ec226c2
-		 * lo_scontext and lo_tcontext are already
ec226c2
-		 * set up.
ec226c2
-		 */
ec226c2
+	context_struct_compute_av(&lo_scontext,
ec226c2
+				  tcontextp,
ec226c2
+				  tclass,
ec226c2
+				  &lo_avd,
ec226c2
+				  NULL);
ec226c2
 
ec226c2
-		context_struct_compute_av(&lo_scontext,
ec226c2
-					  &lo_tcontext,
ec226c2
-					  tclass,
ec226c2
-					  &lo_avd,
ec226c2
-					  NULL);
ec226c2
-		if ((lo_avd.allowed & avd->allowed) == avd->allowed)
ec226c2
-			return;		/* no masked permission */
ec226c2
-		masked = ~lo_avd.allowed & avd->allowed;
ec226c2
-	}
ec226c2
+	masked = ~lo_avd.allowed & avd->allowed;
ec226c2
 
ec226c2
-	if (masked) {
ec226c2
-		/* mask violated permissions */
ec226c2
-		avd->allowed &= ~masked;
ec226c2
+	if (likely(!masked))
ec226c2
+		return;		/* no masked permission */
ec226c2
 
ec226c2
-		/* audit masked permissions */
ec226c2
-		security_dump_masked_av(scontext, tcontext,
ec226c2
-					tclass, masked, "bounds");
ec226c2
-	}
ec226c2
+	/* mask violated permissions */
ec226c2
+	avd->allowed &= ~masked;
ec226c2
+
ec226c2
+	/* audit masked permissions */
ec226c2
+	security_dump_masked_av(scontext, tcontext,
ec226c2
+				tclass, masked, "bounds");
ec226c2
 }
ec226c2
 
ec226c2
 /*
ec226c2
-- 
ec226c2
2.7.4
ec226c2