diff mbox series

[RFC,v14,10/10] landlock: Add user and kernel documentation

Message ID 20200224160215.4136-11-mic@digikod.net (mailing list archive)
State New, archived
Headers show
Series Landlock LSM | expand

Commit Message

Mickaël Salaün Feb. 24, 2020, 4:02 p.m. UTC
This documentation can be built with the Sphinx framework.

Another location might be more appropriate, though.

Signed-off-by: Mickaël Salaün <mic@digikod.net>
Reviewed-by: Vincent Dagonneau <vincent.dagonneau@ssi.gouv.fr>
Cc: Andy Lutomirski <luto@amacapital.net>
Cc: James Morris <jmorris@namei.org>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
---

Changes since v13:
* Rewrote the documentation according to the major revamp.

Previous version:
https://lore.kernel.org/lkml/20191104172146.30797-8-mic@digikod.net/
---
 Documentation/security/index.rst           |   1 +
 Documentation/security/landlock/index.rst  |  18 ++
 Documentation/security/landlock/kernel.rst |  44 ++++
 Documentation/security/landlock/user.rst   | 233 +++++++++++++++++++++
 4 files changed, 296 insertions(+)
 create mode 100644 Documentation/security/landlock/index.rst
 create mode 100644 Documentation/security/landlock/kernel.rst
 create mode 100644 Documentation/security/landlock/user.rst

Comments

Randy Dunlap Feb. 29, 2020, 5:23 p.m. UTC | #1
Hi,
Here are a few corrections for you to consider.


On 2/24/20 8:02 AM, Mickaël Salaün wrote:
> This documentation can be built with the Sphinx framework.
> 
> Another location might be more appropriate, though.
> 
> Signed-off-by: Mickaël Salaün <mic@digikod.net>
> Reviewed-by: Vincent Dagonneau <vincent.dagonneau@ssi.gouv.fr>
> Cc: Andy Lutomirski <luto@amacapital.net>
> Cc: James Morris <jmorris@namei.org>
> Cc: Kees Cook <keescook@chromium.org>
> Cc: Serge E. Hallyn <serge@hallyn.com>
> ---
> 
> Changes since v13:
> * Rewrote the documentation according to the major revamp.
> 
> Previous version:
> https://lore.kernel.org/lkml/20191104172146.30797-8-mic@digikod.net/
> ---
>  Documentation/security/index.rst           |   1 +
>  Documentation/security/landlock/index.rst  |  18 ++
>  Documentation/security/landlock/kernel.rst |  44 ++++
>  Documentation/security/landlock/user.rst   | 233 +++++++++++++++++++++
>  4 files changed, 296 insertions(+)
>  create mode 100644 Documentation/security/landlock/index.rst
>  create mode 100644 Documentation/security/landlock/kernel.rst
>  create mode 100644 Documentation/security/landlock/user.rst
> 
> diff --git a/Documentation/security/landlock/index.rst b/Documentation/security/landlock/index.rst
> new file mode 100644
> index 000000000000..dbd33b96ce60
> --- /dev/null
> +++ b/Documentation/security/landlock/index.rst
> @@ -0,0 +1,18 @@
> +=========================================
> +Landlock LSM: unprivileged access control
> +=========================================
> +
> +:Author: Mickaël Salaün
> +
> +The goal of Landlock is to enable to restrict ambient rights (e.g.  global
> +filesystem access) for a set of processes.  Because Landlock is a stackable
> +LSM, it makes possible to create safe security sandboxes as new security layers
> +in addition to the existing system-wide access-controls. This kind of sandbox
> +is expected to help mitigate the security impact of bugs or
> +unexpected/malicious behaviors in user-space applications. Landlock empower any

                                                                       empowers

> +process, including unprivileged ones, to securely restrict themselves.
> +
> +.. toctree::
> +
> +    user
> +    kernel
> diff --git a/Documentation/security/landlock/kernel.rst b/Documentation/security/landlock/kernel.rst
> new file mode 100644
> index 000000000000..b87769909029
> --- /dev/null
> +++ b/Documentation/security/landlock/kernel.rst
> @@ -0,0 +1,44 @@
> +==============================
> +Landlock: kernel documentation
> +==============================
> +
> +Landlock's goal is to create scoped access-control (i.e. sandboxing).  To
> +harden a whole system, this feature should be available to any process,
> +including unprivileged ones.  Because such process may be compromised or
> +backdoored (i.e. untrusted), Landlock's features must be safe to use from the
> +kernel and other processes point of view.  Landlock's interface must therefore
> +expose a minimal attack surface.
> +
> +Landlock is designed to be usable by unprivileged processes while following the
> +system security policy enforced by other access control mechanisms (e.g. DAC,
> +LSM).  Indeed, a Landlock rule shall not interfere with other access-controls
> +enforced on the system, only add more restrictions.
> +
> +Any user can enforce Landlock rulesets on their processes.  They are merged and
> +evaluated according to the inherited ones in a way that ensure that only more

                                                           ensures

> +constraints can be added.
> +
> +
> +Guiding principles for safe access controls
> +===========================================
> +
> +* A Landlock rule shall be focused on access control on kernel objects instead
> +  of syscall filtering (i.e. syscall arguments), which is the purpose of
> +  seccomp-bpf.
> +* To avoid multiple kind of side-channel attacks (e.g. leak of security

                       kinds

> +  policies, CPU-based attacks), Landlock rules shall not be able to
> +  programmatically communicate with user space.
> +* Kernel access check shall not slow down access request from unsandboxed
> +  processes.
> +* Computation related to Landlock operations (e.g. enforce a ruleset) shall
> +  only impact the processes requesting them.
> +
> +
> +Landlock rulesets and domains
> +=============================
> +
> +A domain is a read-only ruleset tied to a set of subjects (i.e. tasks).  A
> +domain can transition to a new one which is the intersection of the constraints
> +from the current and a new ruleset.  The definition of a subject is implicit
> +for a task sandboxing itself, which makes the reasoning much easier and helps
> +avoid pitfalls.
> diff --git a/Documentation/security/landlock/user.rst b/Documentation/security/landlock/user.rst
> new file mode 100644
> index 000000000000..cbd7f61fca8c
> --- /dev/null
> +++ b/Documentation/security/landlock/user.rst
> @@ -0,0 +1,233 @@
> +=================================
> +Landlock: userspace documentation
> +=================================
> +
> +Landlock rules
> +==============
> +
> +A Landlock rule enables to describe an action on an object.  An object is
> +currently a file hierarchy, and the related filesystem actions are defined in
> +`Access rights`_.  A set of rules are aggregated in a ruleset, which can then

                                     is

> +restricts the thread enforcing it, and its future children.

   restrict

> +
> +
> +Defining and enforcing a security policy
> +----------------------------------------
> +
> +Before defining a security policy, an application should first probe for the
> +features supported by the running kernel, which is important to be compatible
> +with older kernels.  This can be done thanks to the `landlock` syscall (cf.
> +:ref:`syscall`).
> +
> +.. code-block:: c
> +
> +    struct landlock_attr_features attr_features;
> +
> +    if (landlock(LANDLOCK_CMD_GET_FEATURES, LANDLOCK_OPT_GET_FEATURES,
> +            sizeof(attr_features), &attr_features)) {
> +        perror("Failed to probe the Landlock supported features");
> +        return 1;
> +    }
> +
> +Then, we need to create the ruleset that will contains our rules.  For this

                                                 contain

> +example, the ruleset will contains rules which only allow read actions, but

                             contain

> +write actions will be denied.  The ruleset then needs to handle both of these
> +kind of actions.  To have a backward compatibility, these actions should be
> +ANDed with the supported ones.
> +
> +.. code-block:: c
> +
> +    int ruleset_fd;
> +    struct landlock_attr_ruleset ruleset = {
> +        .handled_access_fs =
> +            LANDLOCK_ACCESS_FS_READ |
> +            LANDLOCK_ACCESS_FS_READDIR |
> +            LANDLOCK_ACCESS_FS_EXECUTE |
> +            LANDLOCK_ACCESS_FS_WRITE |
> +            LANDLOCK_ACCESS_FS_TRUNCATE |
> +            LANDLOCK_ACCESS_FS_CHMOD |
> +            LANDLOCK_ACCESS_FS_CHOWN |
> +            LANDLOCK_ACCESS_FS_CHGRP |
> +            LANDLOCK_ACCESS_FS_LINK_TO |
> +            LANDLOCK_ACCESS_FS_RENAME_FROM |
> +            LANDLOCK_ACCESS_FS_RENAME_TO |
> +            LANDLOCK_ACCESS_FS_RMDIR |
> +            LANDLOCK_ACCESS_FS_UNLINK |
> +            LANDLOCK_ACCESS_FS_MAKE_CHAR |
> +            LANDLOCK_ACCESS_FS_MAKE_DIR |
> +            LANDLOCK_ACCESS_FS_MAKE_REG |
> +            LANDLOCK_ACCESS_FS_MAKE_SOCK |
> +            LANDLOCK_ACCESS_FS_MAKE_FIFO |
> +            LANDLOCK_ACCESS_FS_MAKE_BLOCK |
> +            LANDLOCK_ACCESS_FS_MAKE_SYM,
> +    };
> +
> +    ruleset.handled_access_fs &= attr_features.access_fs;
> +    ruleset_fd = landlock(LANDLOCK_CMD_CREATE_RULESET,
> +                    LANDLOCK_OPT_CREATE_RULESET, sizeof(ruleset), &ruleset);
> +    if (ruleset_fd < 0) {
> +        perror("Failed to create a ruleset");
> +        return 1;
> +    }
> +
> +We can now add a new rule to this ruleset thanks to the returned file
> +descriptor referring to this ruleset.  The rule will only enable to read the
> +file hierarchy ``/usr``.  Without other rule, write actions would then be

                             Without other rules,
or
                             Without another rule,

> +denied by the ruleset.  To add ``/usr`` to the ruleset, we open it with the
> +``O_PATH`` flag and fill the &struct landlock_attr_path_beneath with this file
> +descriptor.
> +
> +.. code-block:: c
> +
> +    int err;
> +    struct landlock_attr_path_beneath path_beneath = {
> +        .ruleset_fd = ruleset_fd,
> +        .allowed_access =
> +            LANDLOCK_ACCESS_FS_READ |
> +            LANDLOCK_ACCESS_FS_READDIR |
> +            LANDLOCK_ACCESS_FS_EXECUTE,
> +    };
> +
> +    path_beneath.allowed_access &= attr_features.access_fs;
> +    path_beneath.parent_fd = open("/usr", O_PATH | O_CLOEXEC);
> +    if (path_beneath.parent_fd < 0) {
> +        perror("Failed to open file");
> +        close(ruleset_fd);
> +        return 1;
> +    }
> +    err = landlock(LANDLOCK_CMD_ADD_RULE, LANDLOCK_OPT_ADD_RULE_PATH_BENEATH,
> +            sizeof(path_beneath), &path_beneath);
> +    close(path_beneath.parent_fd);
> +    if (err) {
> +        perror("Failed to update ruleset");
> +        close(ruleset_fd);
> +        return 1;
> +    }
> +
> +We now have a ruleset with one rule allowing read access to ``/usr`` while
> +denying all accesses featured in ``attr_features.access_fs`` to everything else
> +on the filesystem.  The next step is to restrict the current thread from
> +gaining more privileges (e.g. thanks to a SUID binary).
> +
> +.. code-block:: c
> +
> +    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
> +        perror("Failed to restrict privileges");
> +        close(ruleset_fd);
> +        return 1;
> +    }
> +
> +The current thread is now ready to sandbox itself with the ruleset.
> +
> +.. code-block:: c
> +
> +    struct landlock_attr_enforce attr_enforce = {
> +        .ruleset_fd = ruleset_fd,
> +    };
> +
> +    if (landlock(LANDLOCK_CMD_ENFORCE_RULESET, LANDLOCK_OPT_ENFORCE_RULESET,
> +            sizeof(attr_enforce), &attr_enforce)) {
> +        perror("Failed to enforce ruleset");
> +        close(ruleset_fd);
> +        return 1;
> +    }
> +    close(ruleset_fd);
> +
> +If this last system call succeeds, the current thread is now restricted and

   If this last landlock system call succeeds,

[because close() is the last system call]

> +this policy will be enforced on all its subsequently created children as well.
> +Once a thread is landlocked, there is no way to remove its security policy,

                                                   preferably:         policy;

> +only adding more restrictions is allowed.  These threads are now in a new
> +Landlock domain, merge of their parent one (if any) with the new ruleset.
> +
> +A full working code can be found in `samples/landlock/sandboxer.c`_.

   Full working code

> +
> +
> +Inheritance
> +-----------
> +
> +Every new thread resulting from a :manpage:`clone(2)` inherits Landlock program
> +restrictions from its parent.  This is similar to the seccomp inheritance (cf.
> +:doc:`/userspace-api/seccomp_filter`) or any other LSM dealing with task's
> +:manpage:`credentials(7)`.  For instance, one process' thread may apply

                                                 process's

> +Landlock rules to itself, but they will not be automatically applied to other
> +sibling threads (unlike POSIX thread credential changes, cf.
> +:manpage:`nptl(7)`).

[snip]

thanks for the documentation.
Mickaël Salaün March 2, 2020, 10:03 a.m. UTC | #2
On 29/02/2020 18:23, Randy Dunlap wrote:
> Hi,
> Here are a few corrections for you to consider.
> 
> 
> On 2/24/20 8:02 AM, Mickaël Salaün wrote:
>> This documentation can be built with the Sphinx framework.
>>
>> Another location might be more appropriate, though.
>>
>> Signed-off-by: Mickaël Salaün <mic@digikod.net>
>> Reviewed-by: Vincent Dagonneau <vincent.dagonneau@ssi.gouv.fr>
>> Cc: Andy Lutomirski <luto@amacapital.net>
>> Cc: James Morris <jmorris@namei.org>
>> Cc: Kees Cook <keescook@chromium.org>
>> Cc: Serge E. Hallyn <serge@hallyn.com>
>> ---
>>
>> Changes since v13:
>> * Rewrote the documentation according to the major revamp.
>>
>> Previous version:
>> https://lore.kernel.org/lkml/20191104172146.30797-8-mic@digikod.net/
>> ---
>>  Documentation/security/index.rst           |   1 +
>>  Documentation/security/landlock/index.rst  |  18 ++
>>  Documentation/security/landlock/kernel.rst |  44 ++++
>>  Documentation/security/landlock/user.rst   | 233 +++++++++++++++++++++
>>  4 files changed, 296 insertions(+)
>>  create mode 100644 Documentation/security/landlock/index.rst
>>  create mode 100644 Documentation/security/landlock/kernel.rst
>>  create mode 100644 Documentation/security/landlock/user.rst
>>
>> diff --git a/Documentation/security/landlock/index.rst b/Documentation/security/landlock/index.rst
>> new file mode 100644
>> index 000000000000..dbd33b96ce60
>> --- /dev/null
>> +++ b/Documentation/security/landlock/index.rst
>> @@ -0,0 +1,18 @@
>> +=========================================
>> +Landlock LSM: unprivileged access control
>> +=========================================
>> +
>> +:Author: Mickaël Salaün
>> +
>> +The goal of Landlock is to enable to restrict ambient rights (e.g.  global
>> +filesystem access) for a set of processes.  Because Landlock is a stackable
>> +LSM, it makes possible to create safe security sandboxes as new security layers
>> +in addition to the existing system-wide access-controls. This kind of sandbox
>> +is expected to help mitigate the security impact of bugs or
>> +unexpected/malicious behaviors in user-space applications. Landlock empower any
> 
>                                                                        empowers
> 
>> +process, including unprivileged ones, to securely restrict themselves.
>> +
>> +.. toctree::
>> +
>> +    user
>> +    kernel
>> diff --git a/Documentation/security/landlock/kernel.rst b/Documentation/security/landlock/kernel.rst
>> new file mode 100644
>> index 000000000000..b87769909029
>> --- /dev/null
>> +++ b/Documentation/security/landlock/kernel.rst
>> @@ -0,0 +1,44 @@
>> +==============================
>> +Landlock: kernel documentation
>> +==============================
>> +
>> +Landlock's goal is to create scoped access-control (i.e. sandboxing).  To
>> +harden a whole system, this feature should be available to any process,
>> +including unprivileged ones.  Because such process may be compromised or
>> +backdoored (i.e. untrusted), Landlock's features must be safe to use from the
>> +kernel and other processes point of view.  Landlock's interface must therefore
>> +expose a minimal attack surface.
>> +
>> +Landlock is designed to be usable by unprivileged processes while following the
>> +system security policy enforced by other access control mechanisms (e.g. DAC,
>> +LSM).  Indeed, a Landlock rule shall not interfere with other access-controls
>> +enforced on the system, only add more restrictions.
>> +
>> +Any user can enforce Landlock rulesets on their processes.  They are merged and
>> +evaluated according to the inherited ones in a way that ensure that only more
> 
>                                                            ensures
> 
>> +constraints can be added.
>> +
>> +
>> +Guiding principles for safe access controls
>> +===========================================
>> +
>> +* A Landlock rule shall be focused on access control on kernel objects instead
>> +  of syscall filtering (i.e. syscall arguments), which is the purpose of
>> +  seccomp-bpf.
>> +* To avoid multiple kind of side-channel attacks (e.g. leak of security
> 
>                        kinds
> 
>> +  policies, CPU-based attacks), Landlock rules shall not be able to
>> +  programmatically communicate with user space.
>> +* Kernel access check shall not slow down access request from unsandboxed
>> +  processes.
>> +* Computation related to Landlock operations (e.g. enforce a ruleset) shall
>> +  only impact the processes requesting them.
>> +
>> +
>> +Landlock rulesets and domains
>> +=============================
>> +
>> +A domain is a read-only ruleset tied to a set of subjects (i.e. tasks).  A
>> +domain can transition to a new one which is the intersection of the constraints
>> +from the current and a new ruleset.  The definition of a subject is implicit
>> +for a task sandboxing itself, which makes the reasoning much easier and helps
>> +avoid pitfalls.
>> diff --git a/Documentation/security/landlock/user.rst b/Documentation/security/landlock/user.rst
>> new file mode 100644
>> index 000000000000..cbd7f61fca8c
>> --- /dev/null
>> +++ b/Documentation/security/landlock/user.rst
>> @@ -0,0 +1,233 @@
>> +=================================
>> +Landlock: userspace documentation
>> +=================================
>> +
>> +Landlock rules
>> +==============
>> +
>> +A Landlock rule enables to describe an action on an object.  An object is
>> +currently a file hierarchy, and the related filesystem actions are defined in
>> +`Access rights`_.  A set of rules are aggregated in a ruleset, which can then
> 
>                                      is
> 
>> +restricts the thread enforcing it, and its future children.
> 
>    restrict
> 
>> +
>> +
>> +Defining and enforcing a security policy
>> +----------------------------------------
>> +
>> +Before defining a security policy, an application should first probe for the
>> +features supported by the running kernel, which is important to be compatible
>> +with older kernels.  This can be done thanks to the `landlock` syscall (cf.
>> +:ref:`syscall`).
>> +
>> +.. code-block:: c
>> +
>> +    struct landlock_attr_features attr_features;
>> +
>> +    if (landlock(LANDLOCK_CMD_GET_FEATURES, LANDLOCK_OPT_GET_FEATURES,
>> +            sizeof(attr_features), &attr_features)) {
>> +        perror("Failed to probe the Landlock supported features");
>> +        return 1;
>> +    }
>> +
>> +Then, we need to create the ruleset that will contains our rules.  For this
> 
>                                                  contain
> 
>> +example, the ruleset will contains rules which only allow read actions, but
> 
>                              contain
> 
>> +write actions will be denied.  The ruleset then needs to handle both of these
>> +kind of actions.  To have a backward compatibility, these actions should be
>> +ANDed with the supported ones.
>> +
>> +.. code-block:: c
>> +
>> +    int ruleset_fd;
>> +    struct landlock_attr_ruleset ruleset = {
>> +        .handled_access_fs =
>> +            LANDLOCK_ACCESS_FS_READ |
>> +            LANDLOCK_ACCESS_FS_READDIR |
>> +            LANDLOCK_ACCESS_FS_EXECUTE |
>> +            LANDLOCK_ACCESS_FS_WRITE |
>> +            LANDLOCK_ACCESS_FS_TRUNCATE |
>> +            LANDLOCK_ACCESS_FS_CHMOD |
>> +            LANDLOCK_ACCESS_FS_CHOWN |
>> +            LANDLOCK_ACCESS_FS_CHGRP |
>> +            LANDLOCK_ACCESS_FS_LINK_TO |
>> +            LANDLOCK_ACCESS_FS_RENAME_FROM |
>> +            LANDLOCK_ACCESS_FS_RENAME_TO |
>> +            LANDLOCK_ACCESS_FS_RMDIR |
>> +            LANDLOCK_ACCESS_FS_UNLINK |
>> +            LANDLOCK_ACCESS_FS_MAKE_CHAR |
>> +            LANDLOCK_ACCESS_FS_MAKE_DIR |
>> +            LANDLOCK_ACCESS_FS_MAKE_REG |
>> +            LANDLOCK_ACCESS_FS_MAKE_SOCK |
>> +            LANDLOCK_ACCESS_FS_MAKE_FIFO |
>> +            LANDLOCK_ACCESS_FS_MAKE_BLOCK |
>> +            LANDLOCK_ACCESS_FS_MAKE_SYM,
>> +    };
>> +
>> +    ruleset.handled_access_fs &= attr_features.access_fs;
>> +    ruleset_fd = landlock(LANDLOCK_CMD_CREATE_RULESET,
>> +                    LANDLOCK_OPT_CREATE_RULESET, sizeof(ruleset), &ruleset);
>> +    if (ruleset_fd < 0) {
>> +        perror("Failed to create a ruleset");
>> +        return 1;
>> +    }
>> +
>> +We can now add a new rule to this ruleset thanks to the returned file
>> +descriptor referring to this ruleset.  The rule will only enable to read the
>> +file hierarchy ``/usr``.  Without other rule, write actions would then be
> 
>                              Without other rules,
> or
>                              Without another rule,
> 
>> +denied by the ruleset.  To add ``/usr`` to the ruleset, we open it with the
>> +``O_PATH`` flag and fill the &struct landlock_attr_path_beneath with this file
>> +descriptor.
>> +
>> +.. code-block:: c
>> +
>> +    int err;
>> +    struct landlock_attr_path_beneath path_beneath = {
>> +        .ruleset_fd = ruleset_fd,
>> +        .allowed_access =
>> +            LANDLOCK_ACCESS_FS_READ |
>> +            LANDLOCK_ACCESS_FS_READDIR |
>> +            LANDLOCK_ACCESS_FS_EXECUTE,
>> +    };
>> +
>> +    path_beneath.allowed_access &= attr_features.access_fs;
>> +    path_beneath.parent_fd = open("/usr", O_PATH | O_CLOEXEC);
>> +    if (path_beneath.parent_fd < 0) {
>> +        perror("Failed to open file");
>> +        close(ruleset_fd);
>> +        return 1;
>> +    }
>> +    err = landlock(LANDLOCK_CMD_ADD_RULE, LANDLOCK_OPT_ADD_RULE_PATH_BENEATH,
>> +            sizeof(path_beneath), &path_beneath);
>> +    close(path_beneath.parent_fd);
>> +    if (err) {
>> +        perror("Failed to update ruleset");
>> +        close(ruleset_fd);
>> +        return 1;
>> +    }
>> +
>> +We now have a ruleset with one rule allowing read access to ``/usr`` while
>> +denying all accesses featured in ``attr_features.access_fs`` to everything else
>> +on the filesystem.  The next step is to restrict the current thread from
>> +gaining more privileges (e.g. thanks to a SUID binary).
>> +
>> +.. code-block:: c
>> +
>> +    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
>> +        perror("Failed to restrict privileges");
>> +        close(ruleset_fd);
>> +        return 1;
>> +    }
>> +
>> +The current thread is now ready to sandbox itself with the ruleset.
>> +
>> +.. code-block:: c
>> +
>> +    struct landlock_attr_enforce attr_enforce = {
>> +        .ruleset_fd = ruleset_fd,
>> +    };
>> +
>> +    if (landlock(LANDLOCK_CMD_ENFORCE_RULESET, LANDLOCK_OPT_ENFORCE_RULESET,
>> +            sizeof(attr_enforce), &attr_enforce)) {
>> +        perror("Failed to enforce ruleset");
>> +        close(ruleset_fd);
>> +        return 1;
>> +    }
>> +    close(ruleset_fd);
>> +
>> +If this last system call succeeds, the current thread is now restricted and
> 
>    If this last landlock system call succeeds,
> 
> [because close() is the last system call]
> 
>> +this policy will be enforced on all its subsequently created children as well.
>> +Once a thread is landlocked, there is no way to remove its security policy,
> 
>                                                    preferably:         policy;
> 
>> +only adding more restrictions is allowed.  These threads are now in a new
>> +Landlock domain, merge of their parent one (if any) with the new ruleset.
>> +
>> +A full working code can be found in `samples/landlock/sandboxer.c`_.
> 
>    Full working code
> 
>> +
>> +
>> +Inheritance
>> +-----------
>> +
>> +Every new thread resulting from a :manpage:`clone(2)` inherits Landlock program
>> +restrictions from its parent.  This is similar to the seccomp inheritance (cf.
>> +:doc:`/userspace-api/seccomp_filter`) or any other LSM dealing with task's
>> +:manpage:`credentials(7)`.  For instance, one process' thread may apply
> 
>                                                  process's
> 
>> +Landlock rules to itself, but they will not be automatically applied to other
>> +sibling threads (unlike POSIX thread credential changes, cf.
>> +:manpage:`nptl(7)`).
> 
> [snip]
> 
> thanks for the documentation.
> 

Done. Thanks for this attentive review!
diff mbox series

Patch

diff --git a/Documentation/security/index.rst b/Documentation/security/index.rst
index fc503dd689a7..4d213e76ddf4 100644
--- a/Documentation/security/index.rst
+++ b/Documentation/security/index.rst
@@ -15,3 +15,4 @@  Security Documentation
    self-protection
    siphash
    tpm/index
+   landlock/index
diff --git a/Documentation/security/landlock/index.rst b/Documentation/security/landlock/index.rst
new file mode 100644
index 000000000000..dbd33b96ce60
--- /dev/null
+++ b/Documentation/security/landlock/index.rst
@@ -0,0 +1,18 @@ 
+=========================================
+Landlock LSM: unprivileged access control
+=========================================
+
+:Author: Mickaël Salaün
+
+The goal of Landlock is to enable to restrict ambient rights (e.g.  global
+filesystem access) for a set of processes.  Because Landlock is a stackable
+LSM, it makes possible to create safe security sandboxes as new security layers
+in addition to the existing system-wide access-controls. This kind of sandbox
+is expected to help mitigate the security impact of bugs or
+unexpected/malicious behaviors in user-space applications. Landlock empower any
+process, including unprivileged ones, to securely restrict themselves.
+
+.. toctree::
+
+    user
+    kernel
diff --git a/Documentation/security/landlock/kernel.rst b/Documentation/security/landlock/kernel.rst
new file mode 100644
index 000000000000..b87769909029
--- /dev/null
+++ b/Documentation/security/landlock/kernel.rst
@@ -0,0 +1,44 @@ 
+==============================
+Landlock: kernel documentation
+==============================
+
+Landlock's goal is to create scoped access-control (i.e. sandboxing).  To
+harden a whole system, this feature should be available to any process,
+including unprivileged ones.  Because such process may be compromised or
+backdoored (i.e. untrusted), Landlock's features must be safe to use from the
+kernel and other processes point of view.  Landlock's interface must therefore
+expose a minimal attack surface.
+
+Landlock is designed to be usable by unprivileged processes while following the
+system security policy enforced by other access control mechanisms (e.g. DAC,
+LSM).  Indeed, a Landlock rule shall not interfere with other access-controls
+enforced on the system, only add more restrictions.
+
+Any user can enforce Landlock rulesets on their processes.  They are merged and
+evaluated according to the inherited ones in a way that ensure that only more
+constraints can be added.
+
+
+Guiding principles for safe access controls
+===========================================
+
+* A Landlock rule shall be focused on access control on kernel objects instead
+  of syscall filtering (i.e. syscall arguments), which is the purpose of
+  seccomp-bpf.
+* To avoid multiple kind of side-channel attacks (e.g. leak of security
+  policies, CPU-based attacks), Landlock rules shall not be able to
+  programmatically communicate with user space.
+* Kernel access check shall not slow down access request from unsandboxed
+  processes.
+* Computation related to Landlock operations (e.g. enforce a ruleset) shall
+  only impact the processes requesting them.
+
+
+Landlock rulesets and domains
+=============================
+
+A domain is a read-only ruleset tied to a set of subjects (i.e. tasks).  A
+domain can transition to a new one which is the intersection of the constraints
+from the current and a new ruleset.  The definition of a subject is implicit
+for a task sandboxing itself, which makes the reasoning much easier and helps
+avoid pitfalls.
diff --git a/Documentation/security/landlock/user.rst b/Documentation/security/landlock/user.rst
new file mode 100644
index 000000000000..cbd7f61fca8c
--- /dev/null
+++ b/Documentation/security/landlock/user.rst
@@ -0,0 +1,233 @@ 
+=================================
+Landlock: userspace documentation
+=================================
+
+Landlock rules
+==============
+
+A Landlock rule enables to describe an action on an object.  An object is
+currently a file hierarchy, and the related filesystem actions are defined in
+`Access rights`_.  A set of rules are aggregated in a ruleset, which can then
+restricts the thread enforcing it, and its future children.
+
+
+Defining and enforcing a security policy
+----------------------------------------
+
+Before defining a security policy, an application should first probe for the
+features supported by the running kernel, which is important to be compatible
+with older kernels.  This can be done thanks to the `landlock` syscall (cf.
+:ref:`syscall`).
+
+.. code-block:: c
+
+    struct landlock_attr_features attr_features;
+
+    if (landlock(LANDLOCK_CMD_GET_FEATURES, LANDLOCK_OPT_GET_FEATURES,
+            sizeof(attr_features), &attr_features)) {
+        perror("Failed to probe the Landlock supported features");
+        return 1;
+    }
+
+Then, we need to create the ruleset that will contains our rules.  For this
+example, the ruleset will contains rules which only allow read actions, but
+write actions will be denied.  The ruleset then needs to handle both of these
+kind of actions.  To have a backward compatibility, these actions should be
+ANDed with the supported ones.
+
+.. code-block:: c
+
+    int ruleset_fd;
+    struct landlock_attr_ruleset ruleset = {
+        .handled_access_fs =
+            LANDLOCK_ACCESS_FS_READ |
+            LANDLOCK_ACCESS_FS_READDIR |
+            LANDLOCK_ACCESS_FS_EXECUTE |
+            LANDLOCK_ACCESS_FS_WRITE |
+            LANDLOCK_ACCESS_FS_TRUNCATE |
+            LANDLOCK_ACCESS_FS_CHMOD |
+            LANDLOCK_ACCESS_FS_CHOWN |
+            LANDLOCK_ACCESS_FS_CHGRP |
+            LANDLOCK_ACCESS_FS_LINK_TO |
+            LANDLOCK_ACCESS_FS_RENAME_FROM |
+            LANDLOCK_ACCESS_FS_RENAME_TO |
+            LANDLOCK_ACCESS_FS_RMDIR |
+            LANDLOCK_ACCESS_FS_UNLINK |
+            LANDLOCK_ACCESS_FS_MAKE_CHAR |
+            LANDLOCK_ACCESS_FS_MAKE_DIR |
+            LANDLOCK_ACCESS_FS_MAKE_REG |
+            LANDLOCK_ACCESS_FS_MAKE_SOCK |
+            LANDLOCK_ACCESS_FS_MAKE_FIFO |
+            LANDLOCK_ACCESS_FS_MAKE_BLOCK |
+            LANDLOCK_ACCESS_FS_MAKE_SYM,
+    };
+
+    ruleset.handled_access_fs &= attr_features.access_fs;
+    ruleset_fd = landlock(LANDLOCK_CMD_CREATE_RULESET,
+                    LANDLOCK_OPT_CREATE_RULESET, sizeof(ruleset), &ruleset);
+    if (ruleset_fd < 0) {
+        perror("Failed to create a ruleset");
+        return 1;
+    }
+
+We can now add a new rule to this ruleset thanks to the returned file
+descriptor referring to this ruleset.  The rule will only enable to read the
+file hierarchy ``/usr``.  Without other rule, write actions would then be
+denied by the ruleset.  To add ``/usr`` to the ruleset, we open it with the
+``O_PATH`` flag and fill the &struct landlock_attr_path_beneath with this file
+descriptor.
+
+.. code-block:: c
+
+    int err;
+    struct landlock_attr_path_beneath path_beneath = {
+        .ruleset_fd = ruleset_fd,
+        .allowed_access =
+            LANDLOCK_ACCESS_FS_READ |
+            LANDLOCK_ACCESS_FS_READDIR |
+            LANDLOCK_ACCESS_FS_EXECUTE,
+    };
+
+    path_beneath.allowed_access &= attr_features.access_fs;
+    path_beneath.parent_fd = open("/usr", O_PATH | O_CLOEXEC);
+    if (path_beneath.parent_fd < 0) {
+        perror("Failed to open file");
+        close(ruleset_fd);
+        return 1;
+    }
+    err = landlock(LANDLOCK_CMD_ADD_RULE, LANDLOCK_OPT_ADD_RULE_PATH_BENEATH,
+            sizeof(path_beneath), &path_beneath);
+    close(path_beneath.parent_fd);
+    if (err) {
+        perror("Failed to update ruleset");
+        close(ruleset_fd);
+        return 1;
+    }
+
+We now have a ruleset with one rule allowing read access to ``/usr`` while
+denying all accesses featured in ``attr_features.access_fs`` to everything else
+on the filesystem.  The next step is to restrict the current thread from
+gaining more privileges (e.g. thanks to a SUID binary).
+
+.. code-block:: c
+
+    if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
+        perror("Failed to restrict privileges");
+        close(ruleset_fd);
+        return 1;
+    }
+
+The current thread is now ready to sandbox itself with the ruleset.
+
+.. code-block:: c
+
+    struct landlock_attr_enforce attr_enforce = {
+        .ruleset_fd = ruleset_fd,
+    };
+
+    if (landlock(LANDLOCK_CMD_ENFORCE_RULESET, LANDLOCK_OPT_ENFORCE_RULESET,
+            sizeof(attr_enforce), &attr_enforce)) {
+        perror("Failed to enforce ruleset");
+        close(ruleset_fd);
+        return 1;
+    }
+    close(ruleset_fd);
+
+If this last system call succeeds, the current thread is now restricted and
+this policy will be enforced on all its subsequently created children as well.
+Once a thread is landlocked, there is no way to remove its security policy,
+only adding more restrictions is allowed.  These threads are now in a new
+Landlock domain, merge of their parent one (if any) with the new ruleset.
+
+A full working code can be found in `samples/landlock/sandboxer.c`_.
+
+
+Inheritance
+-----------
+
+Every new thread resulting from a :manpage:`clone(2)` inherits Landlock program
+restrictions from its parent.  This is similar to the seccomp inheritance (cf.
+:doc:`/userspace-api/seccomp_filter`) or any other LSM dealing with task's
+:manpage:`credentials(7)`.  For instance, one process' thread may apply
+Landlock rules to itself, but they will not be automatically applied to other
+sibling threads (unlike POSIX thread credential changes, cf.
+:manpage:`nptl(7)`).
+
+
+Ptrace restrictions
+-------------------
+
+A sandboxed process has less privileges than a non-sandboxed process and must
+then be subject to additional restrictions when manipulating another process.
+To be allowed to use :manpage:`ptrace(2)` and related syscalls on a target
+process, a sandboxed process should have a subset of the target process rules,
+which means the tracee must be in a sub-domain of the tracer.
+
+
+.. _syscall:
+
+The `landlock` syscall and its arguments
+========================================
+
+.. kernel-doc:: security/landlock/syscall.c
+    :functions: sys_landlock
+
+Commands
+--------
+
+.. kernel-doc:: include/uapi/linux/landlock.h
+    :functions: landlock_cmd
+
+Options
+-------
+
+.. kernel-doc:: include/uapi/linux/landlock.h
+    :functions: options_intro
+                options_get_features options_create_ruleset
+                options_add_rule options_enforce_ruleset
+
+Attributes
+----------
+
+.. kernel-doc:: include/uapi/linux/landlock.h
+    :functions: landlock_attr_features landlock_attr_ruleset
+                landlock_attr_path_beneath landlock_attr_enforce
+
+Access rights
+-------------
+
+.. kernel-doc:: include/uapi/linux/landlock.h
+    :functions: fs_access
+
+
+Questions and answers
+=====================
+
+What about user space sandbox managers?
+---------------------------------------
+
+Using user space process to enforce restrictions on kernel resources can lead
+to race conditions or inconsistent evaluations (i.e. `Incorrect mirroring of
+the OS code and state
+<https://www.ndss-symposium.org/ndss2003/traps-and-pitfalls-practical-problems-system-call-interposition-based-security-tools/>`_).
+
+What about namespaces and containers?
+-------------------------------------
+
+Namespaces can help create sandboxes but they are not designed for
+access-control and then miss useful features for such use case (e.g. no
+fine-grained restrictions).  Moreover, their complexity can lead to security
+issues, especially when untrusted processes can manipulate them (cf.
+`Controlling access to user namespaces <https://lwn.net/Articles/673597/>`_).
+
+
+Additional documentation
+========================
+
+See https://landlock.io
+
+
+.. Links
+.. _samples/landlock/sandboxer.c: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/samples/landlock/sandboxer.c
+.. _tools/testing/selftests/landlock/: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/landlock/
+.. _tools/testing/selftests/landlock/test_ptrace.c: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/tree/tools/testing/selftests/landlock/test_ptrace.c