[PATCH] selinux: allow to opt-out from skipping kernel sockets in sock_has_perm()

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

 



There is currently a very suspicious condition in sock_has_perm() that
basically skips any permission checking in sock_has_perm() if the target
socket is labeled with SECINITSID_KERNEL. This seems bad for several
reasons:
1. If a user-space process somehow gets its hands on such a socket, it
   will be able to don any socket operation on it without any SELinux
   restriction, even if it is not allowed to do such operations by the
   policy.
2. The exception is inconsistent, since one can e.g. write to a stream
   socket not only via send(2)/sendto(2)/..., but also via write(2),
   which doesn't go through sock_has_perm() and isn't subject to the
   SECINITSID_KERNEL exception.
3. The exception also allows operations on sockets that were created
   before the SELinux policy was loaded (even by user space), since
   these will always inherit the SECINITSID_KERNEL label.

Additionally, it's unclear what is the rationale behind this exception.
For sockets created by the kernel that are expected to be passed to
user space, it seems better to let them undergo normal access checks to
avoid misuse. A possible rationale is to skip checking on operations on
sockets created with kern=1 passed to __sock_create(), which can happen
under user-space credentials even thogh executed internally by the
kernel - notice that such sockets are always labeled with
SECINITSID_KERNEL. However, the operations on these sockets already
normally bypass LSM checks entirely, so arguably this not necessary. On
the contrary, it's better if actual user-space operations on these
sockets go through SELinux checks, since there may be a possibility that
such a socket accidentally leaks into user space and we definitely want
SELinux to detect that and prevent privilege escalation.

Since removing this condition could lead to regressions (notably due to
bullet point (3.) above), add a new policy capability that allows the
policy to opt-out from the condition. This allows policy writers or
distributors to test for impact, add any missing rules, and then enable
the capability.

I tested a kernel with the condition removed on my Fedora workstation
and noted only one new denial, related to a user-space socket created by
systemd-journald before the policy is loaded, which is then continued to
be used by systemd-journald while the system is running.

Also selinux-testsuite is passing without new denials when the check is
removed.

Signed-off-by: Ondrej Mosnacek <omosnace@xxxxxxxxxx>
---
 security/selinux/hooks.c                   | 4 +++-
 security/selinux/include/policycap.h       | 1 +
 security/selinux/include/policycap_names.h | 3 ++-
 security/selinux/include/security.h        | 7 +++++++
 4 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 3c5be76a91991..cf7418df3d4c0 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4561,7 +4561,9 @@ static int sock_has_perm(struct sock *sk, u32 perms)
 	struct common_audit_data ad;
 	struct lsm_network_audit net = {0,};
 
-	if (sksec->sid == SECINITSID_KERNEL)
+	/* legacy behavior was to not perform checks on kernel sockets */
+	if (!selinux_policycap_check_kernel_sockets() &&
+	    sksec->sid == SECINITSID_KERNEL)
 		return 0;
 
 	ad.type = LSM_AUDIT_DATA_NET;
diff --git a/security/selinux/include/policycap.h b/security/selinux/include/policycap.h
index f35d3458e71de..814db520b9d8b 100644
--- a/security/selinux/include/policycap.h
+++ b/security/selinux/include/policycap.h
@@ -12,6 +12,7 @@ enum {
 	POLICYDB_CAP_NNP_NOSUID_TRANSITION,
 	POLICYDB_CAP_GENFS_SECLABEL_SYMLINKS,
 	POLICYDB_CAP_IOCTL_SKIP_CLOEXEC,
+	POLICYDB_CAP_CHECK_KERNEL_SOCKETS,
 	__POLICYDB_CAP_MAX
 };
 #define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)
diff --git a/security/selinux/include/policycap_names.h b/security/selinux/include/policycap_names.h
index 2a87fc3702b81..62de8262f90fe 100644
--- a/security/selinux/include/policycap_names.h
+++ b/security/selinux/include/policycap_names.h
@@ -13,7 +13,8 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
 	"cgroup_seclabel",
 	"nnp_nosuid_transition",
 	"genfs_seclabel_symlinks",
-	"ioctl_skip_cloexec"
+	"ioctl_skip_cloexec",
+	"check_kernel_sockets",
 };
 
 #endif /* _SELINUX_POLICYCAP_NAMES_H_ */
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 393aff41d3ef8..1e57c71d067fb 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -230,6 +230,13 @@ static inline bool selinux_policycap_ioctl_skip_cloexec(void)
 	return READ_ONCE(state->policycap[POLICYDB_CAP_IOCTL_SKIP_CLOEXEC]);
 }
 
+static inline bool selinux_policycap_check_kernel_sockets(void)
+{
+	struct selinux_state *state = &selinux_state;
+
+	return READ_ONCE(state->policycap[POLICYDB_CAP_CHECK_KERNEL_SOCKETS]);
+}
+
 struct selinux_policy_convert_data;
 
 struct selinux_load_state {
-- 
2.39.1




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

  Powered by Linux