[PATCH 0/8] Smack namespace

Lukasz Pawelczyk
2015-05-21T11:54:01Z

Hello,

Some time ago I sent a Smack namespace documentation and a preliminary
LSM namespace for RFC. I've been suggested that there shouldn't be a
separate LSM namespace and that it should live within user namespace.
And this version does. This is a complete set of patches required for
Smack namespace.

This was designed with a collaboration of Smack maintainer Casey
Schaufler.

Smack namespace have been implemented using user namespace hooks added
by one of the patches. To put some context to it I paste here a
documentation on what Smack namespace wants to achieve.

LSM hooks themselves are documented in the security.h file inside the
patch.

The patches are based on:
https://github.com/cschaufler/smack-next/tree/smack-for-4.2-stacked

===================================================================

--- What is a Smack namespace ---

Smack namespace was developed to make it possible for Smack to work
nicely with Linux containers where there is a full operating system
with its own init inside the namespace. Such a system working with
Smack expects to have at least partially working SMACK_MAC_ADMIN to be
able to change labels of processes and files. This is required to be
able to securely start applications under the control of Smack and
manage their access rights.

It was implemented using new LSM hooks added to the user namespace
that were developed together with Smack namespace.

--- Design ideas ---

"Smack namespace" is rather "Smack labels namespace" as not the whole
MAC is namespaced, only the labels. There is a great analogy between
Smack labels namespace and the user namespace part that remaps UIDs.

The idea is to create a map of labels for a namespace so the namespace
is only allowed to use those labels. Smack rules are always the same
as in the init namespace (limited only by what labels are mapped) and
cannot be manipulated from the child namespace. The map is actually
only for labels' names. The underlying structures for labels remain
the same. The filesystem also stores the "unmapped" labels from the
init namespace.

Let's say we have those labels in the init namespace:
label1
label2
label3

and those rules:
label1 label2 rwx
label1 label3 rwx
label2 label3 rwx

We create a map for a namespace:
label1 -> mapped1
label2 -> mapped2

This means that 'label3' is completely invisible in the namespace. As if
it didn't exist. All the rules that include it are ignored.

Effectively in the namespace we have only one rule:
mapped1 mapped2 rwx

Which in reality is:
label1 label2 rwx

All requests to access an object with a 'label3' will be denied. If it
ever comes to a situation where 'label3' would have to be printed
(e.g. reading an exec or mmap label from a file to which we have
access) then huh sign '?' will be printed instead.

All the operations in the namespace on the remaining labels will have
to be performed using their mapped names. Things like changing own
process's label, changing filesystem label. Labels will also be
printed with their mapped names.

You cannot import new labels in a namespace. Every operation that
would do so in an init namespace will return an error in the child
namespace. You cannot assign an unmapped or not existing label to an
object. You can only operate on labels that have been explicitly
mapped.

--- Capabilities ---

Enabling Smack related capabilities (CAP_MAC_ADMIN and
CAP_MAC_OVERRIDE) is main goal of Smack namespace, so it can work
properly in the container. And those capabilities do work to some
extent. In several places where capabilities are checked compatibility
with Smack namespace has been introduced. Capabilities are of course
limited to operate only on mapped labels.

CAP_MAC_OVERRIDE works fully, will allow you to ignore Smack access
rules, but only between objects that have labels mapped. So in the
example above having this CAP will allow e.g. label2 to write to
label1, but will not allow any access to label3.

With CAP_MAC_ADMIN the following operations has been allowed inside
the namespace:
- setting and removing xattr on files, including the security.* ones
- setting process's own label (/proc/self/attr/current)
- mounting in a privileged Smack mode, which means one can specify
additional mount options like: smackfsdef, smackfsfloor etc.

Again this is also allowed only on the mapped labels. Labels on the
filesystem will be stored in unmapped form so they are preserved
through reboots.

Such a namespace construct allows e.g. systemd (with Smack support)
working in a container to assign labels properly to daemons and other
processes.

--- Usage ---

Smack namespace is written using LSM hooks inside user namespace. That
means it's connected to it.

To create a new Smack namespace you need to unshare() user namespace
as usual. If that is all you do though, than there is no difference to
what is now. To activate the Smack namespace you need to fill the
labels' map. It is in a file /proc/$PID/smack_map.

By default the map is empty and Smack namespaces are inactive (labels
are taken directly from a parent namespace). It also means that the
Smack capabilities will be inactive. After you fill the map it starts
to take effect in the namespace and Smack capabilities (only on mapped
labels) start to work.

Due to the way Smack works only CAP_MAC_ADMIN from the parent
namespace (init_user_ns for now, see the "Current limitations" below)
is allowed to fill the map. That means that an unprivileged user is
still allowed to create the user namespace but it will not be able to
fill the labels' map (activate Smack namespace). An administrator
intervention is required.

The attr_map write format is:
unmapped_label mapped_label

When reading the file it shows an active map for a namespace the
process in question is in in the format:
unmapped_label -> mapped_label

If the smack_map file is empty it means the namespace is not mapped
and Smack namespace is inactive (no mappings, MAC related capabilities
behave as they did before, meaning they are active only in
init_user_ns). For init_user_ns the map will always be empty.

Writing to the map file is not disabled after the first write as it is
in uid_map. For Smack we have no means to map ranges of labels, hence
it can really be advantageous to be able to expand the map later
on. But you can only add to the map. You cannot remove already mapped
labels. You cannot change the already existing mappings. Also mappings
has to be 1-1. All requests to create a map where either the unmapped
or the mapped label already exists in the map will be denied.

setns() with Smack namespace active has an additional check that the
label of a process that is calling setns() has to be already mapped in
the target Smack namespace for the call to succeed.

--- Special labels ---

Smack is using some special labels that have built-in rules. Things
like floor '_', dash '^', star '*', etc. Those labels are not
automatically mapped to the namespace. Moreover, you can choose to map
a different label from the init namespace to behave e.g. like floor
inside the namespace.

Let's say we have no rules and those labels in the init namespace:
_
floor_to_be
label

Both 'label' and 'floor_to_be' can read objects with '_'. But they
have no access rights to each other.

Now let's create a map like this:
_ ordinary_label
floor_to_be _
label mapped

Right now label 'mapped' can read label '_' which means that
effectively inside this namespace label 'label' has gained read access
to the 'floor_to_be'. The label 'ordinary_label' is exactly it, an
ordinary label that the built-in rules no longer apply to inside the
namespace.

To sum up, special labels in the namespace behave the same as in the
init namespace. Not the original special labels though, but the ones
we map to specials. This is the only case where a namespace can have
access rights the init namespace does not have (like the 'label' to
'floor_to_be' in the example above).

Of course mappings like these are perfectly legal:
_ _
* *
^ ^

--- Current limitations ---

The Smack namespace is not hierarchical yet. It is currently not
possible to fill a smack_map of a nested user namespace (you can still
create nested user namespace, it will just inherit its parent's map
and won't have active Smack capabilities). When hierarchy will be
implemented the process creating another namespace will be allowed to
map only labels that it has permission to itself (those that it has in
its own map).

Special files inside the virtual smackfs needs to be reviewed whether
it's beneficial to have some of their functionality namespaced as well
(e.g. onlycap, syslog. ambient, etc). This would increase
CAP_MAC_ADMIN privileges inside the namespace.

Lukasz Pawelczyk (8):
kernel/exit.c: make sure current's nsproxy != NULL while checking caps
user_ns: 3 new hooks for LSM namespace operations
smack: extend capability functions and fix 2 checks
smack: abstraction layer for 2 common Smack operations
smack: misc cleanups in preparation for a namespace patch
smack: namespace groundwork
smack: namespace implementation
smack: documentation for the Smack namespace

Documentation/security/00-INDEX | 2 +
Documentation/security/Smack-namespace.txt | 221 ++++++++++++
MAINTAINERS | 1 +
fs/proc/base.c | 57 ++++
include/linux/security.h | 46 +++
include/linux/user_namespace.h | 9 +
kernel/exit.c | 8 +-
kernel/user.c | 3 +
kernel/user_namespace.c | 18 +
security/capability.c | 21 ++
security/security.c | 20 ++
security/smack/Kconfig | 12 +
security/smack/Makefile | 1 +
security/smack/smack.h | 187 +++++++++-
security/smack/smack_access.c | 191 +++++++++--
security/smack/smack_lsm.c | 531 ++++++++++++++++++++---------
security/smack/smack_ns.c | 484 ++++++++++++++++++++++++++
security/smack/smackfs.c | 154 +++++----
18 files changed, 1719 insertions(+), 247 deletions(-)
create mode 100644 Documentation/security/Smack-namespace.txt
create mode 100644 security/smack/smack_ns.c

--
2.1.0