162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Landlock LSM - System call implementations and user space interfaces 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright © 2016-2020 Mickaël Salaün <mic@digikod.net> 662306a36Sopenharmony_ci * Copyright © 2018-2020 ANSSI 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <asm/current.h> 1062306a36Sopenharmony_ci#include <linux/anon_inodes.h> 1162306a36Sopenharmony_ci#include <linux/build_bug.h> 1262306a36Sopenharmony_ci#include <linux/capability.h> 1362306a36Sopenharmony_ci#include <linux/compiler_types.h> 1462306a36Sopenharmony_ci#include <linux/dcache.h> 1562306a36Sopenharmony_ci#include <linux/err.h> 1662306a36Sopenharmony_ci#include <linux/errno.h> 1762306a36Sopenharmony_ci#include <linux/fs.h> 1862306a36Sopenharmony_ci#include <linux/limits.h> 1962306a36Sopenharmony_ci#include <linux/mount.h> 2062306a36Sopenharmony_ci#include <linux/path.h> 2162306a36Sopenharmony_ci#include <linux/sched.h> 2262306a36Sopenharmony_ci#include <linux/security.h> 2362306a36Sopenharmony_ci#include <linux/stddef.h> 2462306a36Sopenharmony_ci#include <linux/syscalls.h> 2562306a36Sopenharmony_ci#include <linux/types.h> 2662306a36Sopenharmony_ci#include <linux/uaccess.h> 2762306a36Sopenharmony_ci#include <uapi/linux/landlock.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#include "cred.h" 3062306a36Sopenharmony_ci#include "fs.h" 3162306a36Sopenharmony_ci#include "limits.h" 3262306a36Sopenharmony_ci#include "ruleset.h" 3362306a36Sopenharmony_ci#include "setup.h" 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic bool is_initialized(void) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci if (likely(landlock_initialized)) 3862306a36Sopenharmony_ci return true; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci pr_warn_once( 4162306a36Sopenharmony_ci "Disabled but requested by user space. " 4262306a36Sopenharmony_ci "You should enable Landlock at boot time: " 4362306a36Sopenharmony_ci "https://docs.kernel.org/userspace-api/landlock.html#boot-time-configuration\n"); 4462306a36Sopenharmony_ci return false; 4562306a36Sopenharmony_ci} 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/** 4862306a36Sopenharmony_ci * copy_min_struct_from_user - Safe future-proof argument copying 4962306a36Sopenharmony_ci * 5062306a36Sopenharmony_ci * Extend copy_struct_from_user() to check for consistent user buffer. 5162306a36Sopenharmony_ci * 5262306a36Sopenharmony_ci * @dst: Kernel space pointer or NULL. 5362306a36Sopenharmony_ci * @ksize: Actual size of the data pointed to by @dst. 5462306a36Sopenharmony_ci * @ksize_min: Minimal required size to be copied. 5562306a36Sopenharmony_ci * @src: User space pointer or NULL. 5662306a36Sopenharmony_ci * @usize: (Alleged) size of the data pointed to by @src. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic __always_inline int 5962306a36Sopenharmony_cicopy_min_struct_from_user(void *const dst, const size_t ksize, 6062306a36Sopenharmony_ci const size_t ksize_min, const void __user *const src, 6162306a36Sopenharmony_ci const size_t usize) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci /* Checks buffer inconsistencies. */ 6462306a36Sopenharmony_ci BUILD_BUG_ON(!dst); 6562306a36Sopenharmony_ci if (!src) 6662306a36Sopenharmony_ci return -EFAULT; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci /* Checks size ranges. */ 6962306a36Sopenharmony_ci BUILD_BUG_ON(ksize <= 0); 7062306a36Sopenharmony_ci BUILD_BUG_ON(ksize < ksize_min); 7162306a36Sopenharmony_ci if (usize < ksize_min) 7262306a36Sopenharmony_ci return -EINVAL; 7362306a36Sopenharmony_ci if (usize > PAGE_SIZE) 7462306a36Sopenharmony_ci return -E2BIG; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci /* Copies user buffer and fills with zeros. */ 7762306a36Sopenharmony_ci return copy_struct_from_user(dst, ksize, src, usize); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci/* 8162306a36Sopenharmony_ci * This function only contains arithmetic operations with constants, leading to 8262306a36Sopenharmony_ci * BUILD_BUG_ON(). The related code is evaluated and checked at build time, 8362306a36Sopenharmony_ci * but it is then ignored thanks to compiler optimizations. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_cistatic void build_check_abi(void) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct landlock_ruleset_attr ruleset_attr; 8862306a36Sopenharmony_ci struct landlock_path_beneath_attr path_beneath_attr; 8962306a36Sopenharmony_ci size_t ruleset_size, path_beneath_size; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * For each user space ABI structures, first checks that there is no 9362306a36Sopenharmony_ci * hole in them, then checks that all architectures have the same 9462306a36Sopenharmony_ci * struct size. 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci ruleset_size = sizeof(ruleset_attr.handled_access_fs); 9762306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(ruleset_attr) != ruleset_size); 9862306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(ruleset_attr) != 8); 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci path_beneath_size = sizeof(path_beneath_attr.allowed_access); 10162306a36Sopenharmony_ci path_beneath_size += sizeof(path_beneath_attr.parent_fd); 10262306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(path_beneath_attr) != path_beneath_size); 10362306a36Sopenharmony_ci BUILD_BUG_ON(sizeof(path_beneath_attr) != 12); 10462306a36Sopenharmony_ci} 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci/* Ruleset handling */ 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int fop_ruleset_release(struct inode *const inode, 10962306a36Sopenharmony_ci struct file *const filp) 11062306a36Sopenharmony_ci{ 11162306a36Sopenharmony_ci struct landlock_ruleset *ruleset = filp->private_data; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci landlock_put_ruleset(ruleset); 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic ssize_t fop_dummy_read(struct file *const filp, char __user *const buf, 11862306a36Sopenharmony_ci const size_t size, loff_t *const ppos) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci /* Dummy handler to enable FMODE_CAN_READ. */ 12162306a36Sopenharmony_ci return -EINVAL; 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic ssize_t fop_dummy_write(struct file *const filp, 12562306a36Sopenharmony_ci const char __user *const buf, const size_t size, 12662306a36Sopenharmony_ci loff_t *const ppos) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci /* Dummy handler to enable FMODE_CAN_WRITE. */ 12962306a36Sopenharmony_ci return -EINVAL; 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * A ruleset file descriptor enables to build a ruleset by adding (i.e. 13462306a36Sopenharmony_ci * writing) rule after rule, without relying on the task's context. This 13562306a36Sopenharmony_ci * reentrant design is also used in a read way to enforce the ruleset on the 13662306a36Sopenharmony_ci * current task. 13762306a36Sopenharmony_ci */ 13862306a36Sopenharmony_cistatic const struct file_operations ruleset_fops = { 13962306a36Sopenharmony_ci .release = fop_ruleset_release, 14062306a36Sopenharmony_ci .read = fop_dummy_read, 14162306a36Sopenharmony_ci .write = fop_dummy_write, 14262306a36Sopenharmony_ci}; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci#define LANDLOCK_ABI_VERSION 3 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/** 14762306a36Sopenharmony_ci * sys_landlock_create_ruleset - Create a new ruleset 14862306a36Sopenharmony_ci * 14962306a36Sopenharmony_ci * @attr: Pointer to a &struct landlock_ruleset_attr identifying the scope of 15062306a36Sopenharmony_ci * the new ruleset. 15162306a36Sopenharmony_ci * @size: Size of the pointed &struct landlock_ruleset_attr (needed for 15262306a36Sopenharmony_ci * backward and forward compatibility). 15362306a36Sopenharmony_ci * @flags: Supported value: %LANDLOCK_CREATE_RULESET_VERSION. 15462306a36Sopenharmony_ci * 15562306a36Sopenharmony_ci * This system call enables to create a new Landlock ruleset, and returns the 15662306a36Sopenharmony_ci * related file descriptor on success. 15762306a36Sopenharmony_ci * 15862306a36Sopenharmony_ci * If @flags is %LANDLOCK_CREATE_RULESET_VERSION and @attr is NULL and @size is 15962306a36Sopenharmony_ci * 0, then the returned value is the highest supported Landlock ABI version 16062306a36Sopenharmony_ci * (starting at 1). 16162306a36Sopenharmony_ci * 16262306a36Sopenharmony_ci * Possible returned errors are: 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 16562306a36Sopenharmony_ci * - %EINVAL: unknown @flags, or unknown access, or too small @size; 16662306a36Sopenharmony_ci * - %E2BIG or %EFAULT: @attr or @size inconsistencies; 16762306a36Sopenharmony_ci * - %ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ciSYSCALL_DEFINE3(landlock_create_ruleset, 17062306a36Sopenharmony_ci const struct landlock_ruleset_attr __user *const, attr, 17162306a36Sopenharmony_ci const size_t, size, const __u32, flags) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci struct landlock_ruleset_attr ruleset_attr; 17462306a36Sopenharmony_ci struct landlock_ruleset *ruleset; 17562306a36Sopenharmony_ci int err, ruleset_fd; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Build-time checks. */ 17862306a36Sopenharmony_ci build_check_abi(); 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci if (!is_initialized()) 18162306a36Sopenharmony_ci return -EOPNOTSUPP; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci if (flags) { 18462306a36Sopenharmony_ci if ((flags == LANDLOCK_CREATE_RULESET_VERSION) && !attr && 18562306a36Sopenharmony_ci !size) 18662306a36Sopenharmony_ci return LANDLOCK_ABI_VERSION; 18762306a36Sopenharmony_ci return -EINVAL; 18862306a36Sopenharmony_ci } 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_ci /* Copies raw user space buffer. */ 19162306a36Sopenharmony_ci err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr), 19262306a36Sopenharmony_ci offsetofend(typeof(ruleset_attr), 19362306a36Sopenharmony_ci handled_access_fs), 19462306a36Sopenharmony_ci attr, size); 19562306a36Sopenharmony_ci if (err) 19662306a36Sopenharmony_ci return err; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Checks content (and 32-bits cast). */ 19962306a36Sopenharmony_ci if ((ruleset_attr.handled_access_fs | LANDLOCK_MASK_ACCESS_FS) != 20062306a36Sopenharmony_ci LANDLOCK_MASK_ACCESS_FS) 20162306a36Sopenharmony_ci return -EINVAL; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* Checks arguments and transforms to kernel struct. */ 20462306a36Sopenharmony_ci ruleset = landlock_create_ruleset(ruleset_attr.handled_access_fs); 20562306a36Sopenharmony_ci if (IS_ERR(ruleset)) 20662306a36Sopenharmony_ci return PTR_ERR(ruleset); 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci /* Creates anonymous FD referring to the ruleset. */ 20962306a36Sopenharmony_ci ruleset_fd = anon_inode_getfd("[landlock-ruleset]", &ruleset_fops, 21062306a36Sopenharmony_ci ruleset, O_RDWR | O_CLOEXEC); 21162306a36Sopenharmony_ci if (ruleset_fd < 0) 21262306a36Sopenharmony_ci landlock_put_ruleset(ruleset); 21362306a36Sopenharmony_ci return ruleset_fd; 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci/* 21762306a36Sopenharmony_ci * Returns an owned ruleset from a FD. It is thus needed to call 21862306a36Sopenharmony_ci * landlock_put_ruleset() on the return value. 21962306a36Sopenharmony_ci */ 22062306a36Sopenharmony_cistatic struct landlock_ruleset *get_ruleset_from_fd(const int fd, 22162306a36Sopenharmony_ci const fmode_t mode) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct fd ruleset_f; 22462306a36Sopenharmony_ci struct landlock_ruleset *ruleset; 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci ruleset_f = fdget(fd); 22762306a36Sopenharmony_ci if (!ruleset_f.file) 22862306a36Sopenharmony_ci return ERR_PTR(-EBADF); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* Checks FD type and access right. */ 23162306a36Sopenharmony_ci if (ruleset_f.file->f_op != &ruleset_fops) { 23262306a36Sopenharmony_ci ruleset = ERR_PTR(-EBADFD); 23362306a36Sopenharmony_ci goto out_fdput; 23462306a36Sopenharmony_ci } 23562306a36Sopenharmony_ci if (!(ruleset_f.file->f_mode & mode)) { 23662306a36Sopenharmony_ci ruleset = ERR_PTR(-EPERM); 23762306a36Sopenharmony_ci goto out_fdput; 23862306a36Sopenharmony_ci } 23962306a36Sopenharmony_ci ruleset = ruleset_f.file->private_data; 24062306a36Sopenharmony_ci if (WARN_ON_ONCE(ruleset->num_layers != 1)) { 24162306a36Sopenharmony_ci ruleset = ERR_PTR(-EINVAL); 24262306a36Sopenharmony_ci goto out_fdput; 24362306a36Sopenharmony_ci } 24462306a36Sopenharmony_ci landlock_get_ruleset(ruleset); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ciout_fdput: 24762306a36Sopenharmony_ci fdput(ruleset_f); 24862306a36Sopenharmony_ci return ruleset; 24962306a36Sopenharmony_ci} 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* Path handling */ 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci/* 25462306a36Sopenharmony_ci * @path: Must call put_path(@path) after the call if it succeeded. 25562306a36Sopenharmony_ci */ 25662306a36Sopenharmony_cistatic int get_path_from_fd(const s32 fd, struct path *const path) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci struct fd f; 25962306a36Sopenharmony_ci int err = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci BUILD_BUG_ON(!__same_type( 26262306a36Sopenharmony_ci fd, ((struct landlock_path_beneath_attr *)NULL)->parent_fd)); 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci /* Handles O_PATH. */ 26562306a36Sopenharmony_ci f = fdget_raw(fd); 26662306a36Sopenharmony_ci if (!f.file) 26762306a36Sopenharmony_ci return -EBADF; 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * Forbids ruleset FDs, internal filesystems (e.g. nsfs), including 27062306a36Sopenharmony_ci * pseudo filesystems that will never be mountable (e.g. sockfs, 27162306a36Sopenharmony_ci * pipefs). 27262306a36Sopenharmony_ci */ 27362306a36Sopenharmony_ci if ((f.file->f_op == &ruleset_fops) || 27462306a36Sopenharmony_ci (f.file->f_path.mnt->mnt_flags & MNT_INTERNAL) || 27562306a36Sopenharmony_ci (f.file->f_path.dentry->d_sb->s_flags & SB_NOUSER) || 27662306a36Sopenharmony_ci d_is_negative(f.file->f_path.dentry) || 27762306a36Sopenharmony_ci IS_PRIVATE(d_backing_inode(f.file->f_path.dentry))) { 27862306a36Sopenharmony_ci err = -EBADFD; 27962306a36Sopenharmony_ci goto out_fdput; 28062306a36Sopenharmony_ci } 28162306a36Sopenharmony_ci *path = f.file->f_path; 28262306a36Sopenharmony_ci path_get(path); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ciout_fdput: 28562306a36Sopenharmony_ci fdput(f); 28662306a36Sopenharmony_ci return err; 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci/** 29062306a36Sopenharmony_ci * sys_landlock_add_rule - Add a new rule to a ruleset 29162306a36Sopenharmony_ci * 29262306a36Sopenharmony_ci * @ruleset_fd: File descriptor tied to the ruleset that should be extended 29362306a36Sopenharmony_ci * with the new rule. 29462306a36Sopenharmony_ci * @rule_type: Identify the structure type pointed to by @rule_attr (only 29562306a36Sopenharmony_ci * %LANDLOCK_RULE_PATH_BENEATH for now). 29662306a36Sopenharmony_ci * @rule_attr: Pointer to a rule (only of type &struct 29762306a36Sopenharmony_ci * landlock_path_beneath_attr for now). 29862306a36Sopenharmony_ci * @flags: Must be 0. 29962306a36Sopenharmony_ci * 30062306a36Sopenharmony_ci * This system call enables to define a new rule and add it to an existing 30162306a36Sopenharmony_ci * ruleset. 30262306a36Sopenharmony_ci * 30362306a36Sopenharmony_ci * Possible returned errors are: 30462306a36Sopenharmony_ci * 30562306a36Sopenharmony_ci * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 30662306a36Sopenharmony_ci * - %EINVAL: @flags is not 0, or inconsistent access in the rule (i.e. 30762306a36Sopenharmony_ci * &landlock_path_beneath_attr.allowed_access is not a subset of the 30862306a36Sopenharmony_ci * ruleset handled accesses); 30962306a36Sopenharmony_ci * - %ENOMSG: Empty accesses (e.g. &landlock_path_beneath_attr.allowed_access); 31062306a36Sopenharmony_ci * - %EBADF: @ruleset_fd is not a file descriptor for the current thread, or a 31162306a36Sopenharmony_ci * member of @rule_attr is not a file descriptor as expected; 31262306a36Sopenharmony_ci * - %EBADFD: @ruleset_fd is not a ruleset file descriptor, or a member of 31362306a36Sopenharmony_ci * @rule_attr is not the expected file descriptor type; 31462306a36Sopenharmony_ci * - %EPERM: @ruleset_fd has no write access to the underlying ruleset; 31562306a36Sopenharmony_ci * - %EFAULT: @rule_attr inconsistency. 31662306a36Sopenharmony_ci */ 31762306a36Sopenharmony_ciSYSCALL_DEFINE4(landlock_add_rule, const int, ruleset_fd, 31862306a36Sopenharmony_ci const enum landlock_rule_type, rule_type, 31962306a36Sopenharmony_ci const void __user *const, rule_attr, const __u32, flags) 32062306a36Sopenharmony_ci{ 32162306a36Sopenharmony_ci struct landlock_path_beneath_attr path_beneath_attr; 32262306a36Sopenharmony_ci struct path path; 32362306a36Sopenharmony_ci struct landlock_ruleset *ruleset; 32462306a36Sopenharmony_ci int res, err; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci if (!is_initialized()) 32762306a36Sopenharmony_ci return -EOPNOTSUPP; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci /* No flag for now. */ 33062306a36Sopenharmony_ci if (flags) 33162306a36Sopenharmony_ci return -EINVAL; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci /* Gets and checks the ruleset. */ 33462306a36Sopenharmony_ci ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_WRITE); 33562306a36Sopenharmony_ci if (IS_ERR(ruleset)) 33662306a36Sopenharmony_ci return PTR_ERR(ruleset); 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (rule_type != LANDLOCK_RULE_PATH_BENEATH) { 33962306a36Sopenharmony_ci err = -EINVAL; 34062306a36Sopenharmony_ci goto out_put_ruleset; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* Copies raw user space buffer, only one type for now. */ 34462306a36Sopenharmony_ci res = copy_from_user(&path_beneath_attr, rule_attr, 34562306a36Sopenharmony_ci sizeof(path_beneath_attr)); 34662306a36Sopenharmony_ci if (res) { 34762306a36Sopenharmony_ci err = -EFAULT; 34862306a36Sopenharmony_ci goto out_put_ruleset; 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci /* 35262306a36Sopenharmony_ci * Informs about useless rule: empty allowed_access (i.e. deny rules) 35362306a36Sopenharmony_ci * are ignored in path walks. 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci if (!path_beneath_attr.allowed_access) { 35662306a36Sopenharmony_ci err = -ENOMSG; 35762306a36Sopenharmony_ci goto out_put_ruleset; 35862306a36Sopenharmony_ci } 35962306a36Sopenharmony_ci /* 36062306a36Sopenharmony_ci * Checks that allowed_access matches the @ruleset constraints 36162306a36Sopenharmony_ci * (ruleset->fs_access_masks[0] is automatically upgraded to 64-bits). 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci if ((path_beneath_attr.allowed_access | ruleset->fs_access_masks[0]) != 36462306a36Sopenharmony_ci ruleset->fs_access_masks[0]) { 36562306a36Sopenharmony_ci err = -EINVAL; 36662306a36Sopenharmony_ci goto out_put_ruleset; 36762306a36Sopenharmony_ci } 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci /* Gets and checks the new rule. */ 37062306a36Sopenharmony_ci err = get_path_from_fd(path_beneath_attr.parent_fd, &path); 37162306a36Sopenharmony_ci if (err) 37262306a36Sopenharmony_ci goto out_put_ruleset; 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci /* Imports the new rule. */ 37562306a36Sopenharmony_ci err = landlock_append_fs_rule(ruleset, &path, 37662306a36Sopenharmony_ci path_beneath_attr.allowed_access); 37762306a36Sopenharmony_ci path_put(&path); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ciout_put_ruleset: 38062306a36Sopenharmony_ci landlock_put_ruleset(ruleset); 38162306a36Sopenharmony_ci return err; 38262306a36Sopenharmony_ci} 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* Enforcement */ 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci/** 38762306a36Sopenharmony_ci * sys_landlock_restrict_self - Enforce a ruleset on the calling thread 38862306a36Sopenharmony_ci * 38962306a36Sopenharmony_ci * @ruleset_fd: File descriptor tied to the ruleset to merge with the target. 39062306a36Sopenharmony_ci * @flags: Must be 0. 39162306a36Sopenharmony_ci * 39262306a36Sopenharmony_ci * This system call enables to enforce a Landlock ruleset on the current 39362306a36Sopenharmony_ci * thread. Enforcing a ruleset requires that the task has %CAP_SYS_ADMIN in its 39462306a36Sopenharmony_ci * namespace or is running with no_new_privs. This avoids scenarios where 39562306a36Sopenharmony_ci * unprivileged tasks can affect the behavior of privileged children. 39662306a36Sopenharmony_ci * 39762306a36Sopenharmony_ci * Possible returned errors are: 39862306a36Sopenharmony_ci * 39962306a36Sopenharmony_ci * - %EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 40062306a36Sopenharmony_ci * - %EINVAL: @flags is not 0. 40162306a36Sopenharmony_ci * - %EBADF: @ruleset_fd is not a file descriptor for the current thread; 40262306a36Sopenharmony_ci * - %EBADFD: @ruleset_fd is not a ruleset file descriptor; 40362306a36Sopenharmony_ci * - %EPERM: @ruleset_fd has no read access to the underlying ruleset, or the 40462306a36Sopenharmony_ci * current thread is not running with no_new_privs, or it doesn't have 40562306a36Sopenharmony_ci * %CAP_SYS_ADMIN in its namespace. 40662306a36Sopenharmony_ci * - %E2BIG: The maximum number of stacked rulesets is reached for the current 40762306a36Sopenharmony_ci * thread. 40862306a36Sopenharmony_ci */ 40962306a36Sopenharmony_ciSYSCALL_DEFINE2(landlock_restrict_self, const int, ruleset_fd, const __u32, 41062306a36Sopenharmony_ci flags) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct landlock_ruleset *new_dom, *ruleset; 41362306a36Sopenharmony_ci struct cred *new_cred; 41462306a36Sopenharmony_ci struct landlock_cred_security *new_llcred; 41562306a36Sopenharmony_ci int err; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci if (!is_initialized()) 41862306a36Sopenharmony_ci return -EOPNOTSUPP; 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_ci /* 42162306a36Sopenharmony_ci * Similar checks as for seccomp(2), except that an -EPERM may be 42262306a36Sopenharmony_ci * returned. 42362306a36Sopenharmony_ci */ 42462306a36Sopenharmony_ci if (!task_no_new_privs(current) && 42562306a36Sopenharmony_ci !ns_capable_noaudit(current_user_ns(), CAP_SYS_ADMIN)) 42662306a36Sopenharmony_ci return -EPERM; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* No flag for now. */ 42962306a36Sopenharmony_ci if (flags) 43062306a36Sopenharmony_ci return -EINVAL; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci /* Gets and checks the ruleset. */ 43362306a36Sopenharmony_ci ruleset = get_ruleset_from_fd(ruleset_fd, FMODE_CAN_READ); 43462306a36Sopenharmony_ci if (IS_ERR(ruleset)) 43562306a36Sopenharmony_ci return PTR_ERR(ruleset); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* Prepares new credentials. */ 43862306a36Sopenharmony_ci new_cred = prepare_creds(); 43962306a36Sopenharmony_ci if (!new_cred) { 44062306a36Sopenharmony_ci err = -ENOMEM; 44162306a36Sopenharmony_ci goto out_put_ruleset; 44262306a36Sopenharmony_ci } 44362306a36Sopenharmony_ci new_llcred = landlock_cred(new_cred); 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci /* 44662306a36Sopenharmony_ci * There is no possible race condition while copying and manipulating 44762306a36Sopenharmony_ci * the current credentials because they are dedicated per thread. 44862306a36Sopenharmony_ci */ 44962306a36Sopenharmony_ci new_dom = landlock_merge_ruleset(new_llcred->domain, ruleset); 45062306a36Sopenharmony_ci if (IS_ERR(new_dom)) { 45162306a36Sopenharmony_ci err = PTR_ERR(new_dom); 45262306a36Sopenharmony_ci goto out_put_creds; 45362306a36Sopenharmony_ci } 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* Replaces the old (prepared) domain. */ 45662306a36Sopenharmony_ci landlock_put_ruleset(new_llcred->domain); 45762306a36Sopenharmony_ci new_llcred->domain = new_dom; 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci landlock_put_ruleset(ruleset); 46062306a36Sopenharmony_ci return commit_creds(new_cred); 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ciout_put_creds: 46362306a36Sopenharmony_ci abort_creds(new_cred); 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ciout_put_ruleset: 46662306a36Sopenharmony_ci landlock_put_ruleset(ruleset); 46762306a36Sopenharmony_ci return err; 46862306a36Sopenharmony_ci} 469