Landlock: kernel documentation

eBPF properties

To get an expressive language while still being safe and small, Landlock is based on eBPF. Landlock should be usable by untrusted processes and must therefore expose a minimal attack surface. The eBPF bytecode is minimal, powerful, widely used and designed to be used by untrusted applications. Thus, reusing the eBPF support in the kernel enables a generic approach while minimizing new code.

An eBPF program has access to an eBPF context containing some fields used to inspect the current object. These arguments may be used directly (e.g. raw value) or passed to helper functions according to their types (e.g. pointer). It is then possible to do complex access checks without race conditions or inconsistent evaluation (i.e. incorrect mirroring of the OS code and state).

A Landlock hook describes a particular access type. For now, there is one hook dedicated to ptrace related operations: BPF_LANDLOCK_PTRACE. A Landlock program is tied to one hook. This makes it possible to statically check context accesses, potentially performed by such program, and hence prevents kernel address leaks and ensure the right use of hook arguments with eBPF functions. Any user can add multiple Landlock programs per Landlock hook. They are stacked and evaluated one after the other, starting from the most recent program, as seccomp-bpf does with its filters. Underneath, a hook is an abstraction over a set of LSM hooks.

Guiding principles

Unprivileged use

  • Landlock helpers and context should be usable by any unprivileged and untrusted program while following the system security policy enforced by other access control mechanisms (e.g. DAC, LSM), even if a global CAP_SYS_ADMIN is currently required.

Landlock hook and context

  • A Landlock hook shall be focused on access control on kernel objects instead of syscall filtering (i.e. syscall arguments), which is the purpose of seccomp-bpf.
  • A Landlock context provided by a hook shall express the minimal and more generic interface to control an access for a kernel object.
  • A hook shall guaranty that all the BPF function calls from a program are safe. Thus, the related Landlock context arguments shall always be of the same type for a particular hook. For example, a network hook could share helpers with a file hook because of UNIX socket. However, the same helpers may not be compatible for a file system handle and a net handle.
  • Multiple hooks may use the same context interface.

Landlock helpers

  • Landlock helpers shall be as generic as possible while at the same time being as simple as possible and following the syscall creation principles (cf. Documentation/adding-syscalls.txt).
  • The only behavior change allowed on a helper is to fix a (logical) bug to match the initial semantic.
  • Helpers shall be reentrant, i.e. only take inputs from arguments (e.g. from the BPF context), to enable a hook to use a cache. Future program options might change this cache behavior.
  • It is quite easy to add new helpers to extend Landlock. The main concern should be about the possibility to leak information from the kernel that may not be accessible otherwise (i.e. side-channel attack).

Landlock domain

A Landlock domain is a set of eBPF programs. There is a list for each different program types that can be run on a specific Landlock hook (e.g. ptrace). A domain is tied to a set of subjects (i.e. tasks).

A Landlock program should not try (nor be able) to infer which subject is currently enforced, but to have a unique security policy for all subjects tied to the same domain. This make the reasoning much easier and help avoid pitfalls.

struct landlock_domain

Landlock programs enforced on a set of tasks

Definition

struct landlock_domain {
  struct landlock_prog_list *programs[_LANDLOCK_HOOK_LAST];
  refcount_t usage;
};

Members

programs
array of non-NULL struct landlock_prog_list pointers
usage
reference count to manage the object lifetime. When a task needs to add Landlock programs and if usage is greater than 1, then the task must duplicate struct landlock_domain to not change the children’s programs as well.

Description

When prepending a new program, if struct landlock_domain is shared with other tasks, then duplicate it and prepend the program to this new struct landlock_domain.

struct landlock_domain * landlock_prepend_prog(struct landlock_domain * current_domain, struct bpf_prog * prog)

attach a Landlock prog_list to current_domain

Parameters

struct landlock_domain * current_domain
landlock_domain pointer, must be (RCU-)locked (if needed) to prevent a concurrent put/free. This pointer must not be freed after the call.
struct bpf_prog * prog
non-NULL Landlock prog_list to prepend to current_domain. prog will be owned by landlock_prepend_prog() and freed if an error happened.

Description

Whatever is the result of this function call, you can call bpf_prog_put(prog) after.

Return current_domain or a new pointer when OK. Return a pointer error otherwise.

Adding a Landlock program with seccomp

The seccomp(2) syscall can be used with the SECCOMP_PREPEND_LANDLOCK_PROG operation to prepend a Landlock program to the current task’s domain.

int landlock_seccomp_prepend_prog(unsigned int flags, const int __user * user_bpf_fd)

attach a Landlock program to the current task

Parameters

unsigned int flags
not used, must be 0
const int __user * user_bpf_fd
file descriptor pointing to a loaded Landlock prog

Description

current->cred->security[landlock]->domain is lazily allocated. When a new credential is created, only a pointer is copied. When a new Landlock program is added by a task, if there is other references to this task’s domain, then a new allocation is made to contain an array pointing to Landlock program lists. This design enable low-performance impact and is memory efficient while keeping the property of prepend-only programs.

For now, installing a Landlock program requires that the requesting task has the global CAP_SYS_ADMIN. We cannot force the use of no_new_privs to not exclude containers where a process may legitimately acquire more privileges thanks to an SUID binary.

Running a list of Landlock programs

bool landlock_access_denied(struct landlock_domain * domain, struct landlock_hook_ctx * hook_ctx)

run Landlock programs tied to a hook

Parameters

struct landlock_domain * domain
Landlock domain pointer
struct landlock_hook_ctx * hook_ctx
non-NULL valid eBPF context pointer

Description

Return true if at least one program return deny, false otherwise.

LSM hooks

int hook_ptrace_access_check(struct task_struct * child, unsigned int mode)

determine whether the current process may access another

Parameters

struct task_struct * child
the process to be accessed
unsigned int mode
the mode of attachment

Description

If the current task (i.e. tracer) has one or multiple BPF_LANDLOCK_PTRACE programs, then run them with the struct landlock_context_ptrace context. If one of these programs return LANDLOCK_RET_DENY, then deny access with -EPERM, else allow it by returning 0.

int hook_ptrace_traceme(struct task_struct * parent)

determine whether another process may trace the current one

Parameters

struct task_struct * parent
the task proposed to be the tracer

Description

If the parent task (i.e. tracer) has one or multiple BPF_LANDLOCK_PTRACE programs, then run them with the struct landlock_context_ptrace context. If one of these programs return LANDLOCK_RET_DENY, then deny access with -EPERM, else allow it by returning 0.

Questions and answers

Why a program does not return an errno or a kill code?

seccomp filters can return multiple kind of code, including an errno value or a kill signal, which may be convenient for access control. Those return codes are hardwired in the userland ABI. Instead, Landlock’s approach is to return a bitmask to allow or deny an action, which is much simpler and more generic. Moreover, we do not really have a choice because, unlike to seccomp, Landlock programs are not enforced at the syscall entry point but may be executed at any point in the kernel (through LSM hooks) where an errno return code may not make sense. However, with this simple ABI and with the ability to call helpers, Landlock may gain features similar to seccomp-bpf in the future while being compatible with previous programs.