Landlock: userland documentation

Landlock programs

eBPF programs are used to create security programs. They are contained and can call only a whitelist of dedicated functions. Moreover, they can only loop under strict conditions, which protects from denial of service. More information on BPF can be found in Documentation/networking/filter.txt.

Writing a program

To enforce a security policy, a thread first needs to create a Landlock program. The easiest way to write an eBPF program depicting a security program is to write it in the C language. As described in samples/bpf/README.rst, LLVM can compile such programs. Files samples/bpf/landlock1_kern.c and those in tools/testing/selftests/landlock/ can be used as examples.

Once the eBPF program is created, the next step is to create the metadata describing the Landlock program. This metadata includes a subtype which contains the hook type to which the program is tied and some options.

union bpf_prog_subtype subtype = {
    .landlock_hook = {
        .type = LANDLOCK_HOOK_FS_PICK,
        .triggers = LANDLOCK_TRIGGER_FS_PICK_OPEN,
    }
};

A Landlock hook describes the kind of kernel object for which a program will be triggered to allow or deny an action. For example, the hook LANDLOCK_HOOK_FS_PICK can be triggered every time a landlocked thread performs a set of action related to the filesystem (e.g. open, read, write, mount…). This actions are identified by the triggers bitfield.

The next step is to fill a union bpf_attr with BPF_PROG_TYPE_LANDLOCK_HOOK, the previously created subtype and other BPF program metadata. This bpf_attr must then be passed to the bpf(2) syscall alongside the BPF_PROG_LOAD command. If everything is deemed correct by the kernel, the thread gets a file descriptor referring to this program.

In the following code, the insn variable is an array of BPF instructions which can be extracted from an ELF file as is done in bpf_load_file() from samples/bpf/bpf_load.c.

union bpf_attr attr = {
    .prog_type = BPF_PROG_TYPE_LANDLOCK_HOOK,
    .insn_cnt = sizeof(insn) / sizeof(struct bpf_insn),
    .insns = (__u64) (unsigned long) insn,
    .license = (__u64) (unsigned long) "GPL",
    .prog_subtype = &subtype,
    .prog_subtype_size = sizeof(subtype),
};
int fd = bpf(BPF_PROG_LOAD, &attr, sizeof(attr));
if (fd == -1)
    exit(1);

Enforcing a program

Once the Landlock program has been created or received (e.g. through a UNIX socket), the thread willing to sandbox itself (and its future children) should perform the following two steps.

The thread should first request to never be allowed to get new privileges with a call to prctl(2) and the PR_SET_NO_NEW_PRIVS option. More information can be found in Documentation/prctl/no_new_privs.txt.

if (prctl(PR_SET_NO_NEW_PRIVS, 1, NULL, 0, 0))
    exit(1);

A thread can apply a program to itself by using the seccomp(2) syscall. The operation is SECCOMP_PREPEND_LANDLOCK_PROG, the flags must be empty and the args argument must point to a valid Landlock program file descriptor.

if (seccomp(SECCOMP_PREPEND_LANDLOCK_PROG, 0, &fd))
    exit(1);

If the syscall succeeds, the program is now enforced on the calling thread and will be enforced on all its subsequently created children of the thread as well. Once a thread is landlocked, there is no way to remove this security policy, only stacking more restrictions is allowed. The program evaluation is performed from the newest to the oldest.

When a syscall ask for an action on a kernel object, if this action is denied, then an EACCES errno code is returned through the syscall.

Inherited programs

Every new thread resulting from a clone(2) inherits Landlock program restrictions from its parent. This is similar to the seccomp inheritance as described in Documentation/prctl/seccomp_filter.txt.

Ptrace restrictions

A landlocked process has less privileges than a non-landlocked process and must then be subject to additional restrictions when manipulating another process. To be allowed to use ptrace(2) and related syscalls on a target process, a landlocked process must have a subset of the target process programs.

Landlock structures and constants

Hook types

enum landlock_hook_type

hook type for which a Landlock program is called

Constants

LANDLOCK_HOOK_FS_PICK
called for the last element of a file path
LANDLOCK_HOOK_FS_WALK
called for each directory of a file path (excluding the directory passed to fs_pick, if any)

Description

A hook is a policy decision point which exposes the same context type for each program evaluation.

Contexts

struct landlock_ctx_fs_pick

context accessible to a fs_pick program

Definition

struct landlock_ctx_fs_pick {
  __u64 inode;
};

Members

inode
pointer to the current kernel object that can be used to compare inodes from an inode map.
struct landlock_ctx_fs_walk

context accessible to a fs_walk program

Definition

struct landlock_ctx_fs_walk {
  __u64 inode;
};

Members

inode
pointer to the current kernel object that can be used to compare inodes from an inode map.

Triggers for fs_pick

A landlock trigger is used as a bitmask in subtype.landlock_hook.triggers for a fs_pick program. It defines a set of actions for which the program should verify an access request.

  • LANDLOCK_TRIGGER_FS_PICK_APPEND
  • LANDLOCK_TRIGGER_FS_PICK_CHDIR
  • LANDLOCK_TRIGGER_FS_PICK_CHROOT
  • LANDLOCK_TRIGGER_FS_PICK_CREATE
  • LANDLOCK_TRIGGER_FS_PICK_EXECUTE
  • LANDLOCK_TRIGGER_FS_PICK_FCNTL
  • LANDLOCK_TRIGGER_FS_PICK_GETATTR
  • LANDLOCK_TRIGGER_FS_PICK_IOCTL
  • LANDLOCK_TRIGGER_FS_PICK_LINK
  • LANDLOCK_TRIGGER_FS_PICK_LINKTO
  • LANDLOCK_TRIGGER_FS_PICK_LOCK
  • LANDLOCK_TRIGGER_FS_PICK_MAP
  • LANDLOCK_TRIGGER_FS_PICK_MOUNTON
  • LANDLOCK_TRIGGER_FS_PICK_OPEN
  • LANDLOCK_TRIGGER_FS_PICK_READ
  • LANDLOCK_TRIGGER_FS_PICK_READDIR
  • LANDLOCK_TRIGGER_FS_PICK_RECEIVE
  • LANDLOCK_TRIGGER_FS_PICK_RENAME
  • LANDLOCK_TRIGGER_FS_PICK_RENAMETO
  • LANDLOCK_TRIGGER_FS_PICK_RMDIR
  • LANDLOCK_TRIGGER_FS_PICK_SETATTR
  • LANDLOCK_TRIGGER_FS_PICK_TRANSFER
  • LANDLOCK_TRIGGER_FS_PICK_UNLINK
  • LANDLOCK_TRIGGER_FS_PICK_WRITE

Additional documentation

See https://landlock.io