Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

landlock: Enable user space to infer supported features

Add a new flag LANDLOCK_CREATE_RULESET_VERSION to
landlock_create_ruleset(2). This enables to retreive a Landlock ABI
version that is useful to efficiently follow a best-effort security
approach. Indeed, it would be a missed opportunity to abort the whole
sandbox building, because some features are unavailable, instead of
protecting users as much as possible with the subset of features
provided by the running kernel.

This new flag enables user space to identify the minimum set of Landlock
features supported by the running kernel without relying on a filesystem
interface (e.g. /proc/version, which might be inaccessible) nor testing
multiple syscall argument combinations (i.e. syscall bisection). New
Landlock features will be documented and tied to a minimum version
number (greater than 1). The current version will be incremented for
each new kernel release supporting new Landlock features. User space
libraries can leverage this information to seamlessly restrict processes
as much as possible while being compatible with newer APIs.

This is a much more lighter approach than the previous
landlock_get_features(2): the complexity is pushed to user space
libraries. This flag meets similar needs as securityfs versions:
selinux/policyvers, apparmor/features/*/version* and tomoyo/version.

Supporting this flag now will be convenient for backward compatibility.

Cc: Arnd Bergmann <arnd@arndb.de>
Cc: James Morris <jmorris@namei.org>
Cc: Jann Horn <jannh@google.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Serge E. Hallyn <serge@hallyn.com>
Signed-off-by: Mickaël Salaün <mic@linux.microsoft.com>
Link: https://lore.kernel.org/r/20210422154123.13086-14-mic@digikod.net
Signed-off-by: James Morris <jamorris@linux.microsoft.com>

authored by

Mickaël Salaün and committed by
James Morris
3532b0b4 5526b450

+68 -4
+8
include/uapi/linux/landlock.h
··· 27 27 __u64 handled_access_fs; 28 28 }; 29 29 30 + /* 31 + * sys_landlock_create_ruleset() flags: 32 + * 33 + * - %LANDLOCK_CREATE_RULESET_VERSION: Get the highest supported Landlock ABI 34 + * version. 35 + */ 36 + #define LANDLOCK_CREATE_RULESET_VERSION (1U << 0) 37 + 30 38 /** 31 39 * enum landlock_rule_type - Landlock rule type 32 40 *
+13 -4
security/landlock/syscalls.c
··· 128 128 .write = fop_dummy_write, 129 129 }; 130 130 131 + #define LANDLOCK_ABI_VERSION 1 132 + 131 133 /** 132 134 * sys_landlock_create_ruleset - Create a new ruleset 133 135 * ··· 137 135 * the new ruleset. 138 136 * @size: Size of the pointed &struct landlock_ruleset_attr (needed for 139 137 * backward and forward compatibility). 140 - * @flags: Must be 0. 138 + * @flags: Supported value: %LANDLOCK_CREATE_RULESET_VERSION. 141 139 * 142 140 * This system call enables to create a new Landlock ruleset, and returns the 143 141 * related file descriptor on success. 144 142 * 143 + * If @flags is %LANDLOCK_CREATE_RULESET_VERSION and @attr is NULL and @size is 144 + * 0, then the returned value is the highest supported Landlock ABI version 145 + * (starting at 1). 146 + * 145 147 * Possible returned errors are: 146 148 * 147 149 * - EOPNOTSUPP: Landlock is supported by the kernel but disabled at boot time; 148 - * - EINVAL: @flags is not 0, or unknown access, or too small @size; 150 + * - EINVAL: unknown @flags, or unknown access, or too small @size; 149 151 * - E2BIG or EFAULT: @attr or @size inconsistencies; 150 152 * - ENOMSG: empty &landlock_ruleset_attr.handled_access_fs. 151 153 */ ··· 167 161 if (!landlock_initialized) 168 162 return -EOPNOTSUPP; 169 163 170 - /* No flag for now. */ 171 - if (flags) 164 + if (flags) { 165 + if ((flags == LANDLOCK_CREATE_RULESET_VERSION) 166 + && !attr && !size) 167 + return LANDLOCK_ABI_VERSION; 172 168 return -EINVAL; 169 + } 173 170 174 171 /* Copies raw user space buffer. */ 175 172 err = copy_min_struct_from_user(&ruleset_attr, sizeof(ruleset_attr),
+47
tools/testing/selftests/landlock/base_test.c
··· 63 63 free(buf); 64 64 } 65 65 66 + TEST(abi_version) { 67 + const struct landlock_ruleset_attr ruleset_attr = { 68 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 69 + }; 70 + ASSERT_EQ(1, landlock_create_ruleset(NULL, 0, 71 + LANDLOCK_CREATE_RULESET_VERSION)); 72 + 73 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, 74 + LANDLOCK_CREATE_RULESET_VERSION)); 75 + ASSERT_EQ(EINVAL, errno); 76 + 77 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr), 78 + LANDLOCK_CREATE_RULESET_VERSION)); 79 + ASSERT_EQ(EINVAL, errno); 80 + 81 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 82 + sizeof(ruleset_attr), 83 + LANDLOCK_CREATE_RULESET_VERSION)); 84 + ASSERT_EQ(EINVAL, errno); 85 + 86 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, 87 + LANDLOCK_CREATE_RULESET_VERSION | 1 << 31)); 88 + ASSERT_EQ(EINVAL, errno); 89 + } 90 + 91 + TEST(inval_create_ruleset_flags) { 92 + const int last_flag = LANDLOCK_CREATE_RULESET_VERSION; 93 + const int invalid_flag = last_flag << 1; 94 + const struct landlock_ruleset_attr ruleset_attr = { 95 + .handled_access_fs = LANDLOCK_ACCESS_FS_READ_FILE, 96 + }; 97 + 98 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, 0, invalid_flag)); 99 + ASSERT_EQ(EINVAL, errno); 100 + 101 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 0, invalid_flag)); 102 + ASSERT_EQ(EINVAL, errno); 103 + 104 + ASSERT_EQ(-1, landlock_create_ruleset(NULL, sizeof(ruleset_attr), 105 + invalid_flag)); 106 + ASSERT_EQ(EINVAL, errno); 107 + 108 + ASSERT_EQ(-1, landlock_create_ruleset(&ruleset_attr, 109 + sizeof(ruleset_attr), invalid_flag)); 110 + ASSERT_EQ(EINVAL, errno); 111 + } 112 + 66 113 TEST(empty_path_beneath_attr) { 67 114 const struct landlock_ruleset_attr ruleset_attr = { 68 115 .handled_access_fs = LANDLOCK_ACCESS_FS_EXECUTE,