Mailing List Archive

SELinux: Granting kernel_t (kdevtmpfs) manage rights on /dev/*
Hi all

I have a situation that I'd like to hear your opinion on.

In bug #535992 a what seems like simple problem is asking for quite some
work. It covers a currently cosmetic denial (i.e. SELinux is preventing
something but that does not seem to have any noticeable impact on the
system) regarding kdevtmpfs trying to work with device files.

Now you'll say - duh, that is what kdevtmpfs is for: working with the device
files in /dev (which is a devtmpfs mount). Exactly my thoughts. So I wanted
to include a policy that allows kernel_t (which is the domain that kdevtmpfs
works in) to manage all device nodes in /dev using the
dev_manage_all_dev_nodes() interface.

But here is the problem: that interface also assigns a set of attributes to
the (kernel_t) domain so that a number of neverallow rules are not violated.
Sadly, the kernel_t domain is part of the base module, meaning that it
cannot depend on definitions that are not part of the base module either
(isolation requirement). The attributes however are part of the "storage"
module, which is not part of base.

Simple - let's make storage part of base, right? Not that that is hard, but
we're in the middle of a migration between userspaces (2.3 versus 2.4) which
puts in some additional problems: with userspace 2.4, new policy modules are
loaded on priority level 400 whereas olders are at priority 100.

Now you'll say - whoah priorities? Really? What's that about?

Well, this is a new feature in the 2.4 userspace. Instead of using policy
module versions (which is now no longer supported in the 2.4 userspace) the
userspace now supports priorities. SELinux policy modules can be loaded at
higher priorities (and thus become active) while the lower priorities are
still in the SELinux store. If the higher priority module is unloaded, the
lower priority module becomes active again.

And this is the problem that we'll get: the older storage module (at
priority 100, or even at priority 400 for those using ~arch systems for a
while now) might still become active when we load the new policies (which no
longer would include the storage module). So we need to make sure that this
never happens, preferably by removing all modules from priority 100 to begin
with, and to remove the storage module when we are loading the new policies.

(╯°□°)╯︵ ┻━┻
-- "Screw that!"

Finally, that's a solution. And it can even be automated (the patch is
below), but I'm afraid that there might be situations where the automated
approach does not work, and we are not able to teach most Gentoo/SELinux
users to get at the same SELinux managing level as us SELinux-related
developers.

So I have the following conundrum.

1. I can temporarily ignore the issue, perhaps hiding the cosmetic denial
behind dontaudit statements
2. I can restrictively add to kernel_t those rules that do not trigger the
neverallow rules and ignore/dontaudit the rest
3. I can break isolation a bit and explicitly add kernel_t to the neverallow
rule exemption
4. I can move the necessary attributes and statements into the devices
module (which is part of the base)
5. I can move forward with the storage-becomes-base approach

Using 5 to me is the most beautiful solution, but quite intensive. The lack
of understanding of priorities and the entire matter might confuse users
currently (2.4 is too new for that).

Using 4 requires some rework which will make it harder to follow upstream
for the affected modules (storage and devices most likely, perhaps also kernel)
(as the changes involve changing existing modules, not just adding new
rules).

Using 3 is simpler, almost a no-brainer, but is not upstreamable (as it
breaks modular isolation). More for quick solutions to improve the situation
while working on a better solution (option 5?)

Using 2 and 1 do not really implement the solution (and thus don't resolve
the issue). I think that upstream will also prefer 2/1 because there is no
visible impact of kdevtmpfs not having all these accesses. But I personally
think that this is a matter of specific use cases (which we just have not
hit yet) and not within the expectations of the kdevtmpfs code itself.

I'll also prod upstream about the issue of course, but this I also like to
discuss for Gentoo in detail.

The fix for the checks on selinux-base-policy is below:


Index: selinux-base-policy-9999.ebuild
===================================================================
RCS file: /var/cvsroot/gentoo-x86/sec-policy/selinux-base-policy/selinux-base-policy-9999.ebuild,v
retrieving revision 1.21
diff -u -B -r1.21 selinux-base-policy-9999.ebuild
--- selinux-base-policy-9999.ebuild 7 Dec 2014 13:21:06 -0000 1.21
+++ selinux-base-policy-9999.ebuild 4 Mar 2015 19:30:00 -0000
@@ -1,4 +1,4 @@
-# Copyright 1999-2014 Gentoo Foundation
+# Copyright 1999-2015 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-x86/sec-policy/selinux-base-policy/selinux-base-policy-9999.ebuild,v 1.21 2014/12/07 13:21:06 perfinion Exp $
EAPI="5"
@@ -28,7 +28,7 @@
PDEPEND="unconfined? ( sec-policy/selinux-unconfined )"
DEPEND=""

-MODS="application authlogin bootloader clock consoletype cron dmesg fstools getty hostname hotplug init iptables libraries locallogin logging lvm miscfiles modutils mount mta netutils nscd portage raid rsync selinuxutil setrans ssh staff storage su sysadm sysnetwork tmpfiles udev userdomain usermanage unprivuser xdg"
+MODS="application authlogin bootloader clock consoletype cron dmesg fstools getty hostname hotplug init iptables libraries locallogin logging lvm miscfiles modutils mount mta netutils nscd portage raid rsync selinuxutil setrans ssh staff su sysadm sysnetwork tmpfiles udev userdomain usermanage unprivuser xdg"
LICENSE="GPL-2"
SLOT="0"
S="${WORKDIR}/"
@@ -122,13 +122,92 @@
COMMAND="-i ${i}.pp ${COMMAND}"
done

- for i in ${POLICY_TYPES}; do
- einfo "Inserting the following modules, with base, into the $i module store: ${MODS}"
+ # Check if we are using 2.4 userspace or not
+ # Clunky check because semodule does not have a --version and we want
+ # to check semodule's version support, not semanage or other tools.
+ semodule --help | grep -q "priority"
+ if [ $? -eq 0 ] ; then
+ # Priority support found so we are using 2.4 userspace
+ # Before continuing, check that all modules of priority=100 are gone
+ for i in ${POLICY_TYPES} ; do
+ semodule -s ${i} --list-modules=full | grep -q "^100 storage"
+ if [ $? -eq 0 ] ; then
+ # Still a storage module at priority 100 found.
+ # Is a set already loaded at priority 400?
+ semodule -s ${i} --list-modules=full | grep -q "^400 base"
+ if [ $? -eq 0 ] ; then
+ # Also a set at 400.
+ # Let's clear those at priority 100.
+ local CURMODLIST=$(semodule -s ${i} --priority=100 -l);
+ ewarn "A full set of SELinux policy modules is running at priority 400 (which is good)"
+ ewarn "but also at priority 100. The latter should be removed from the system as we"
+ ewarn "might get into collisions (especially with the storage module). Trying to clear:"
+ semodule -s ${i} --priority=100 -r ${CURMODLIST};
+ if [ $? -eq 0 ] ; then
+ # Priority 100 cleared.
+ einfo "All modules on priority 100 have been removed. We can now continue safely."
+ else
+ eerror "It was not possible to remove the modules at priority 100. Please ensure that"
+ eerror "there are no modules at this priority left. In extreme cases it might be"
+ eerror "necessary to create an empty module to override an older one before removing"
+ eerror "the older module."
+ eerror "Use 'semodule --priority=100 -r modulename' to remove modules."
+ eerror "Use 'semodule --list-modules=full' to list all modules/priority sets"
+ die "Could not clear SELinux policy modules at priority level 100"
+ fi
+ else
+ # None at priority 400 yet. We need to load the current set at priority 100 first
+ # while removing the storage module, and then reload at priority 400,
+ semodule -s ${i} --priority=100 -i base.pp ${COMMAND} -r storage
+ if [ $? -ne 0 ] ; then
+ # Failed to load at priority 100.
+ eerror "We have detected that the storage module is loaded with priority 100 but no full"
+ eerror "policy set yet at priority 400 (which is default for the 2.4 userspace). Recent"
+ eerror "policies no longer contain the storage module (it has become part of the base"
+ eerror "module) which might result in dependency problems with the currently-loaded"
+ eerror "storage module at priority 100."
+ eerror "Sadly we were not able to automatically resolve this. Please make sure that no"
+ eerror "SELinux policy modules are running with priority 100 anymore."
+ die "Could not load policies at priority 100 in order to remove the active storage module"
+ fi
+ fi
+ else
+ # No storage module at priority 100 anymore
+ # Check if there is a storage module at priority 400.
+ semodule -s ${i} --list-modules=full | grep -q "^400 storage"
+ if [ $? -eq 0 ] ; then
+ # Storage module running at priority 400
+ # Load the new base set while removing the storage module
+ semodule -s ${i} -i base.pp ${COMMAND} -r storage
+ if [ $? -ne 0 ] ; then
+ # Could not reload while removing storage module
+ eerror "A running storage SELinux module is found but could not be removed. Recent"
+ eerror "policies no longer contain the storage module (it has become part of the base"
+ eerror "module) which might result in dependency problems with the currently-loaded"
+ eerror "storage module."
+ eerror "Sadly we were not able to automatically resolve this. Please make sure that no"
+ eerror "SELinux policy modules are running anymore."
+ die "Could not remove storage module from the policy store."
+ fi
+ fi
+ fi
+ # Now try to load the new set
+ einfo "Inserting the following modules, with base, into the $i module store: ${MODS}"

- cd /usr/share/selinux/${i} || die "Could not enter /usr/share/selinux/${i}"
+ cd /usr/share/selinux/${i} || die "Could not enter /usr/share/selinux/${i}"

- semodule -s ${i} -b base.pp ${COMMAND} || die "Failed to load in base and modules ${MODS} in the $i policy store"
- done
+ semodule -s ${i} -i base.pp ${COMMAND} || die "Failed to load in base and modules \"${MODS}\" in the $i policy store"
+ done
+ else
+ # No priority support so not using 2.4 userspace
+ for i in ${POLICY_TYPES}; do
+ einfo "Inserting the following modules, with base, into the $i module store: ${MODS}"
+
+ cd /usr/share/selinux/${i} || die "Could not enter /usr/share/selinux/${i}"
+
+ semodule -s ${i} -b base.pp ${COMMAND} || die "Failed to load in base and modules ${MODS} in the $i policy store"
+ done
+ fi

# Relabel depending packages
local PKGSET="";
Re: SELinux: Granting kernel_t (kdevtmpfs) manage rights on /dev/* [ In reply to ]
On Wed, 4 Mar 2015 20:21:08 +0000
Sven Vermeulen <swift@gentoo.org> wrote:

> 1. I can temporarily ignore the issue, perhaps hiding the cosmetic
> denial behind dontaudit statements
> 2. I can restrictively add to kernel_t those rules that do not
> trigger the neverallow rules and ignore/dontaudit the rest
> 3. I can break isolation a bit and explicitly add kernel_t to the
> neverallow rule exemption
> 4. I can move the necessary attributes and statements into the devices
> module (which is part of the base)
> 5. I can move forward with the storage-becomes-base approach

I've been allowing this in my local policy since 2013. I'm sure it was
neccessary for something to work, however I don't recall what for. But
that means 1. is not really an option.

For now, I'd just wait for more feedback on the refpolicy ML. This is
not an urgent problem, so I'd prefer not to diverge further from
upstream if we can avoid it.

5. seems to be the cleanest solution, but I've got to dig around a bit
in the refpolicy to estimate the amount of work it'd require.

If we want a temporary fix, I'd go with 3. It's only a tiny change, so
it wouldn't cause too much confusing upstream divergence.



--
Luis Ressel <aranea@aixah.de>
GPG fpr: F08D 2AF6 655E 25DE 52BC E53D 08F5 7F90 3029 B5BD
Re: SELinux: Granting kernel_t (kdevtmpfs) manage rights on /dev/* [ In reply to ]
On Wed, Mar 04, 2015 at 11:04:34PM +0100, Luis Ressel wrote:
> On Wed, 4 Mar 2015 20:21:08 +0000
> Sven Vermeulen <swift@gentoo.org> wrote:
>
> > 1. I can temporarily ignore the issue, perhaps hiding the cosmetic
> > denial behind dontaudit statements
> > 2. I can restrictively add to kernel_t those rules that do not
> > trigger the neverallow rules and ignore/dontaudit the rest
> > 3. I can break isolation a bit and explicitly add kernel_t to the
> > neverallow rule exemption
> > 4. I can move the necessary attributes and statements into the devices
> > module (which is part of the base)
> > 5. I can move forward with the storage-becomes-base approach
>
> I've been allowing this in my local policy since 2013. I'm sure it was
> neccessary for something to work, however I don't recall what for. But
> that means 1. is not really an option.
>
> For now, I'd just wait for more feedback on the refpolicy ML. This is
> not an urgent problem, so I'd prefer not to diverge further from
> upstream if we can avoid it.

I also think we should deviate as little as possible from upstream.
>
> 5. seems to be the cleanest solution, but I've got to dig around a bit
> in the refpolicy to estimate the amount of work it'd require.
>
> If we want a temporary fix, I'd go with 3. It's only a tiny change, so
> it wouldn't cause too much confusing upstream divergence.

I dont think this is a very high priority issue so we can probably
upstream it first and then fix it in gentoo. Storage becoming base
makes sense since storage is pretty important. The patch in the email
seemed reasonable. although i think we'd also need to add to the eclass
that when inserting a new module it will check priority 100 and remove
it after inserting the new module.

-- Jason