Skip to content

Commit

Permalink
Merge tag 'landlock-6.2-rc1' of git://git.kernel.org/pub/scm/linux/ke…
Browse files Browse the repository at this point in the history
…rnel/git/mic/linux

Pull landlock updates from Mickaël Salaün:
 "This adds file truncation support to Landlock, contributed by Günther
  Noack. As described by Günther [1], the goal of these patches is to
  work towards a more complete coverage of file system operations that
  are restrictable with Landlock.

  The known set of currently unsupported file system operations in
  Landlock is described at [2]. Out of the operations listed there,
  truncate is the only one that modifies file contents, so these patches
  should make it possible to prevent the direct modification of file
  contents with Landlock.

  The new LANDLOCK_ACCESS_FS_TRUNCATE access right covers both the
  truncate(2) and ftruncate(2) families of syscalls, as well as open(2)
  with the O_TRUNC flag. This includes usages of creat() in the case
  where existing regular files are overwritten.

  Additionally, this introduces a new Landlock security blob associated
  with opened files, to track the available Landlock access rights at
  the time of opening the file. This is in line with Unix's general
  approach of checking the read and write permissions during open(), and
  associating this previously checked authorization with the opened
  file. An ongoing patch documents this use case [3].

  In order to treat truncate(2) and ftruncate(2) calls differently in an
  LSM hook, we split apart the existing security_path_truncate hook into
  security_path_truncate (for truncation by path) and
  security_file_truncate (for truncation of previously opened files)"

Link: https://lore.kernel.org/r/[email protected] [1]
Link: https://www.kernel.org/doc/html/v6.1/userspace-api/landlock.html#filesystem-flags [2]
Link: https://lore.kernel.org/r/[email protected] [3]

* tag 'landlock-6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/mic/linux:
  samples/landlock: Document best-effort approach for LANDLOCK_ACCESS_FS_REFER
  landlock: Document Landlock's file truncation support
  samples/landlock: Extend sample tool to support LANDLOCK_ACCESS_FS_TRUNCATE
  selftests/landlock: Test ftruncate on FDs created by memfd_create(2)
  selftests/landlock: Test FD passing from restricted to unrestricted processes
  selftests/landlock: Locally define __maybe_unused
  selftests/landlock: Test open() and ftruncate() in multiple scenarios
  selftests/landlock: Test file truncation support
  landlock: Support file truncation
  landlock: Document init_layer_masks() helper
  landlock: Refactor check_access_path_dual() into is_access_to_paths_allowed()
  security: Create file_truncate hook from path_truncate hook
  • Loading branch information
torvalds committed Dec 13, 2022
2 parents e529d35 + f6e53fb commit 299e2b1
Show file tree
Hide file tree
Showing 19 changed files with 878 additions and 121 deletions.
67 changes: 60 additions & 7 deletions Documentation/userspace-api/landlock.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Landlock: unprivileged access control
=====================================

:Author: Mickaël Salaün
:Date: September 2022
:Date: October 2022

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
Expand Down Expand Up @@ -60,7 +60,8 @@ the need to be explicit about the denied-by-default access rights.
LANDLOCK_ACCESS_FS_MAKE_FIFO |
LANDLOCK_ACCESS_FS_MAKE_BLOCK |
LANDLOCK_ACCESS_FS_MAKE_SYM |
LANDLOCK_ACCESS_FS_REFER,
LANDLOCK_ACCESS_FS_REFER |
LANDLOCK_ACCESS_FS_TRUNCATE,
};
Because we may not know on which kernel version an application will be
Expand All @@ -69,16 +70,28 @@ should try to protect users as much as possible whatever the kernel they are
using. To avoid binary enforcement (i.e. either all security features or
none), we can leverage a dedicated Landlock command to get the current version
of the Landlock ABI and adapt the handled accesses. Let's check if we should
remove the ``LANDLOCK_ACCESS_FS_REFER`` access right which is only supported
starting with the second version of the ABI.
remove the ``LANDLOCK_ACCESS_FS_REFER`` or ``LANDLOCK_ACCESS_FS_TRUNCATE``
access rights, which are only supported starting with the second and third
version of the ABI.

.. code-block:: c
int abi;
abi = landlock_create_ruleset(NULL, 0, LANDLOCK_CREATE_RULESET_VERSION);
if (abi < 2) {
if (abi < 0) {
/* Degrades gracefully if Landlock is not handled. */
perror("The running kernel does not enable to use Landlock");
return 0;
}
switch (abi) {
case 1:
/* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
__attribute__((fallthrough));
case 2:
/* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;
}
This enables to create an inclusive ruleset that will contain our rules.
Expand Down Expand Up @@ -127,8 +140,8 @@ descriptor.
It may also be required to create rules following the same logic as explained
for the ruleset creation, by filtering access rights according to the Landlock
ABI version. In this example, this is not required because
``LANDLOCK_ACCESS_FS_REFER`` is not allowed by any rule.
ABI version. In this example, this is not required because all of the requested
``allowed_access`` rights are already available in ABI 1.

We now have a ruleset with one rule allowing read access to ``/usr`` while
denying all other handled accesses for the filesystem. The next step is to
Expand Down Expand Up @@ -252,6 +265,37 @@ 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.

Truncating files
----------------

The operations covered by ``LANDLOCK_ACCESS_FS_WRITE_FILE`` and
``LANDLOCK_ACCESS_FS_TRUNCATE`` both change the contents of a file and sometimes
overlap in non-intuitive ways. It is recommended to always specify both of
these together.

A particularly surprising example is :manpage:`creat(2)`. The name suggests
that this system call requires the rights to create and write files. However,
it also requires the truncate right if an existing file under the same name is
already present.

It should also be noted that truncating files does not require the
``LANDLOCK_ACCESS_FS_WRITE_FILE`` right. Apart from the :manpage:`truncate(2)`
system call, this can also be done through :manpage:`open(2)` with the flags
``O_RDONLY | O_TRUNC``.

When opening a file, the availability of the ``LANDLOCK_ACCESS_FS_TRUNCATE``
right is associated with the newly created file descriptor and will be used for
subsequent truncation attempts using :manpage:`ftruncate(2)`. The behavior is
similar to opening a file for reading or writing, where permissions are checked
during :manpage:`open(2)`, but not during the subsequent :manpage:`read(2)` and
:manpage:`write(2)` calls.

As a consequence, it is possible to have multiple open file descriptors for the
same file, where one grants the right to truncate the file and the other does
not. It is also possible to pass such file descriptors between processes,
keeping their Landlock properties, even when these processes do not have an
enforced Landlock ruleset.

Compatibility
=============

Expand Down Expand Up @@ -398,6 +442,15 @@ Starting with the Landlock ABI version 2, it is now possible to securely
control renaming and linking thanks to the new ``LANDLOCK_ACCESS_FS_REFER``
access right.

File truncation (ABI < 3)
-------------------------

File truncation could not be denied before the third Landlock ABI, so it is
always allowed when using a kernel that only supports the first or second ABI.

Starting with the Landlock ABI version 3, it is now possible to securely control
truncation thanks to the new ``LANDLOCK_ACCESS_FS_TRUNCATE`` access right.

.. _kernel_support:

Kernel support
Expand Down
2 changes: 1 addition & 1 deletion fs/namei.c
Original file line number Diff line number Diff line change
Expand Up @@ -3211,7 +3211,7 @@ static int handle_truncate(struct user_namespace *mnt_userns, struct file *filp)
if (error)
return error;

error = security_path_truncate(path);
error = security_file_truncate(filp);
if (!error) {
error = do_truncate(mnt_userns, path->dentry, 0,
ATTR_MTIME|ATTR_CTIME|ATTR_OPEN,
Expand Down
2 changes: 1 addition & 1 deletion fs/open.c
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ long do_sys_ftruncate(unsigned int fd, loff_t length, int small)
if (IS_APPEND(file_inode(f.file)))
goto out_putf;
sb_start_write(inode->i_sb);
error = security_path_truncate(&f.file->f_path);
error = security_file_truncate(f.file);
if (!error)
error = do_truncate(file_mnt_user_ns(f.file), dentry, length,
ATTR_MTIME | ATTR_CTIME, f.file);
Expand Down
1 change: 1 addition & 0 deletions include/linux/lsm_hook_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ LSM_HOOK(int, 0, file_send_sigiotask, struct task_struct *tsk,
struct fown_struct *fown, int sig)
LSM_HOOK(int, 0, file_receive, struct file *file)
LSM_HOOK(int, 0, file_open, struct file *file)
LSM_HOOK(int, 0, file_truncate, struct file *file)
LSM_HOOK(int, 0, task_alloc, struct task_struct *task,
unsigned long clone_flags)
LSM_HOOK(void, LSM_RET_VOID, task_free, struct task_struct *task)
Expand Down
10 changes: 9 additions & 1 deletion include/linux/lsm_hooks.h
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,9 @@
* @attr is the iattr structure containing the new file attributes.
* Return 0 if permission is granted.
* @path_truncate:
* Check permission before truncating a file.
* Check permission before truncating the file indicated by path.
* Note that truncation permissions may also be checked based on
* already opened files, using the @file_truncate hook.
* @path contains the path structure for the file.
* Return 0 if permission is granted.
* @inode_getattr:
Expand Down Expand Up @@ -610,6 +612,12 @@
* to receive an open file descriptor via socket IPC.
* @file contains the file structure being received.
* Return 0 if permission is granted.
* @file_truncate:
* Check permission before truncating a file, i.e. using ftruncate.
* Note that truncation permission may also be checked based on the path,
* using the @path_truncate hook.
* @file contains the file structure for the file.
* Return 0 if permission is granted.
* @file_open:
* Save open-time permission checking state for later use upon
* file_permission, and recheck access if anything has changed
Expand Down
6 changes: 6 additions & 0 deletions include/linux/security.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,7 @@ int security_file_send_sigiotask(struct task_struct *tsk,
struct fown_struct *fown, int sig);
int security_file_receive(struct file *file);
int security_file_open(struct file *file);
int security_file_truncate(struct file *file);
int security_task_alloc(struct task_struct *task, unsigned long clone_flags);
void security_task_free(struct task_struct *task);
int security_cred_alloc_blank(struct cred *cred, gfp_t gfp);
Expand Down Expand Up @@ -1043,6 +1044,11 @@ static inline int security_file_open(struct file *file)
return 0;
}

static inline int security_file_truncate(struct file *file)
{
return 0;
}

static inline int security_task_alloc(struct task_struct *task,
unsigned long clone_flags)
{
Expand Down
21 changes: 16 additions & 5 deletions include/uapi/linux/landlock.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,19 @@ struct landlock_path_beneath_attr {
* A file can only receive these access rights:
*
* - %LANDLOCK_ACCESS_FS_EXECUTE: Execute a file.
* - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access.
* - %LANDLOCK_ACCESS_FS_WRITE_FILE: Open a file with write access. Note that
* you might additionally need the %LANDLOCK_ACCESS_FS_TRUNCATE right in order
* to overwrite files with :manpage:`open(2)` using ``O_TRUNC`` or
* :manpage:`creat(2)`.
* - %LANDLOCK_ACCESS_FS_READ_FILE: Open a file with read access.
* - %LANDLOCK_ACCESS_FS_TRUNCATE: Truncate a file with :manpage:`truncate(2)`,
* :manpage:`ftruncate(2)`, :manpage:`creat(2)`, or :manpage:`open(2)` with
* ``O_TRUNC``. Whether an opened file can be truncated with
* :manpage:`ftruncate(2)` is determined during :manpage:`open(2)`, in the
* same way as read and write permissions are checked during
* :manpage:`open(2)` using %LANDLOCK_ACCESS_FS_READ_FILE and
* %LANDLOCK_ACCESS_FS_WRITE_FILE. This access right is available since the
* third version of the Landlock ABI.
*
* A directory can receive access rights related to files or directories. The
* following access right is applied to the directory itself, and the
Expand Down Expand Up @@ -139,10 +150,9 @@ struct landlock_path_beneath_attr {
*
* It is currently not possible to restrict some file-related actions
* accessible through these syscall families: :manpage:`chdir(2)`,
* :manpage:`truncate(2)`, :manpage:`stat(2)`, :manpage:`flock(2)`,
* :manpage:`chmod(2)`, :manpage:`chown(2)`, :manpage:`setxattr(2)`,
* :manpage:`utime(2)`, :manpage:`ioctl(2)`, :manpage:`fcntl(2)`,
* :manpage:`access(2)`.
* :manpage:`stat(2)`, :manpage:`flock(2)`, :manpage:`chmod(2)`,
* :manpage:`chown(2)`, :manpage:`setxattr(2)`, :manpage:`utime(2)`,
* :manpage:`ioctl(2)`, :manpage:`fcntl(2)`, :manpage:`access(2)`.
* Future Landlock evolutions will enable to restrict them.
*/
/* clang-format off */
Expand All @@ -160,6 +170,7 @@ struct landlock_path_beneath_attr {
#define LANDLOCK_ACCESS_FS_MAKE_BLOCK (1ULL << 11)
#define LANDLOCK_ACCESS_FS_MAKE_SYM (1ULL << 12)
#define LANDLOCK_ACCESS_FS_REFER (1ULL << 13)
#define LANDLOCK_ACCESS_FS_TRUNCATE (1ULL << 14)
/* clang-format on */

#endif /* _UAPI_LINUX_LANDLOCK_H */
29 changes: 25 additions & 4 deletions samples/landlock/sandboxer.c
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ static int parse_path(char *env_path, const char ***const path_list)
#define ACCESS_FILE ( \
LANDLOCK_ACCESS_FS_EXECUTE | \
LANDLOCK_ACCESS_FS_WRITE_FILE | \
LANDLOCK_ACCESS_FS_READ_FILE)
LANDLOCK_ACCESS_FS_READ_FILE | \
LANDLOCK_ACCESS_FS_TRUNCATE)

/* clang-format on */

Expand Down Expand Up @@ -160,11 +161,12 @@ static int populate_ruleset(const char *const env_var, const int ruleset_fd,
LANDLOCK_ACCESS_FS_MAKE_FIFO | \
LANDLOCK_ACCESS_FS_MAKE_BLOCK | \
LANDLOCK_ACCESS_FS_MAKE_SYM | \
LANDLOCK_ACCESS_FS_REFER)
LANDLOCK_ACCESS_FS_REFER | \
LANDLOCK_ACCESS_FS_TRUNCATE)

/* clang-format on */

#define LANDLOCK_ABI_LAST 2
#define LANDLOCK_ABI_LAST 3

int main(const int argc, char *const argv[], char *const *const envp)
{
Expand Down Expand Up @@ -232,8 +234,27 @@ int main(const int argc, char *const argv[], char *const *const envp)
/* Best-effort security. */
switch (abi) {
case 1:
/* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2 */
/*
* Removes LANDLOCK_ACCESS_FS_REFER for ABI < 2
*
* Note: The "refer" operations (file renaming and linking
* across different directories) are always forbidden when using
* Landlock with ABI 1.
*
* If only ABI 1 is available, this sandboxer knowingly forbids
* refer operations.
*
* If a program *needs* to do refer operations after enabling
* Landlock, it can not use Landlock at ABI level 1. To be
* compatible with different kernel versions, such programs
* should then fall back to not restrict themselves at all if
* the running kernel only supports ABI 1.
*/
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_REFER;
__attribute__((fallthrough));
case 2:
/* Removes LANDLOCK_ACCESS_FS_TRUNCATE for ABI < 3 */
ruleset_attr.handled_access_fs &= ~LANDLOCK_ACCESS_FS_TRUNCATE;

fprintf(stderr,
"Hint: You should update the running kernel "
Expand Down
6 changes: 6 additions & 0 deletions security/apparmor/lsm.c
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,11 @@ static int apparmor_path_truncate(const struct path *path)
return common_perm_cond(OP_TRUNC, path, MAY_WRITE | AA_MAY_SETATTR);
}

static int apparmor_file_truncate(struct file *file)
{
return apparmor_path_truncate(&file->f_path);
}

static int apparmor_path_symlink(const struct path *dir, struct dentry *dentry,
const char *old_name)
{
Expand Down Expand Up @@ -1241,6 +1246,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
LSM_HOOK_INIT(mmap_file, apparmor_mmap_file),
LSM_HOOK_INIT(file_mprotect, apparmor_file_mprotect),
LSM_HOOK_INIT(file_lock, apparmor_file_lock),
LSM_HOOK_INIT(file_truncate, apparmor_file_truncate),

LSM_HOOK_INIT(getprocattr, apparmor_getprocattr),
LSM_HOOK_INIT(setprocattr, apparmor_setprocattr),
Expand Down
Loading

0 comments on commit 299e2b1

Please sign in to comment.