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