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

selftests/landlock: Fix capability for net_test

CAP_NET_ADMIN allows to configure network interfaces, not CAP_SYS_ADMIN
which only allows to call unshare(2). Without this change, running
network tests as a non-root user but with all capabilities would fail at
the setup_loopback() step with "RTNETLINK answers: Operation not
permitted".

The issue is only visible when running tests with non-root users (i.e.
only relying on ambient capabilities). Indeed, when configuring the
network interface, the "ip" command is called, which may lead to the
special handling of capabilities for the root user by execve(2). If
root is the caller, then the inherited, permitted and effective
capabilities are all reset, which then includes CAP_NET_ADMIN. However,
if a non-root user is the caller, then ambient capabilities are masked
by the inherited ones, which were explicitly dropped.

To make execution deterministic whatever users are running the tests,
set the noroot secure bit for each test, and set the inheritable and
ambient capabilities to CAP_NET_ADMIN, the only capability that may be
required after an execve(2).

Factor out _effective_cap() into _change_cap(), and use it to manage
ambient capabilities with the new set_ambient_cap() and
clear_ambient_cap() helpers.

This makes it possible to run all Landlock tests with check-linux.sh
from https://github.com/landlock-lsm/landlock-test-tools

Cc: Konstantin Meskhidze <konstantin.meskhidze@huawei.com>
Fixes: a549d055a22e ("selftests/landlock: Add network tests")
Link: https://lore.kernel.org/r/20240125153230.3817165-2-mic@digikod.net
[mic: Make sure SECBIT_NOROOT_LOCKED is set]
Signed-off-by: Mickaël Salaün <mic@digikod.net>

+44 -9
+40 -8
tools/testing/selftests/landlock/common.h
··· 9 9 10 10 #include <errno.h> 11 11 #include <linux/landlock.h> 12 + #include <linux/securebits.h> 12 13 #include <sys/capability.h> 13 14 #include <sys/socket.h> 14 15 #include <sys/syscall.h> ··· 116 115 /* clang-format off */ 117 116 CAP_DAC_OVERRIDE, 118 117 CAP_MKNOD, 118 + CAP_NET_ADMIN, 119 + CAP_NET_BIND_SERVICE, 119 120 CAP_SYS_ADMIN, 120 121 CAP_SYS_CHROOT, 121 - CAP_NET_BIND_SERVICE, 122 122 /* clang-format on */ 123 123 }; 124 + const unsigned int noroot = SECBIT_NOROOT | SECBIT_NOROOT_LOCKED; 125 + 126 + if ((cap_get_secbits() & noroot) != noroot) 127 + EXPECT_EQ(0, cap_set_secbits(noroot)); 124 128 125 129 cap_p = cap_get_proc(); 126 130 EXPECT_NE(NULL, cap_p) ··· 143 137 TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 144 138 } 145 139 } 140 + 141 + /* Automatically resets ambient capabilities. */ 146 142 EXPECT_NE(-1, cap_set_proc(cap_p)) 147 143 { 148 144 TH_LOG("Failed to cap_set_proc: %s", strerror(errno)); ··· 153 145 { 154 146 TH_LOG("Failed to cap_free: %s", strerror(errno)); 155 147 } 148 + 149 + /* Quickly checks that ambient capabilities are cleared. */ 150 + EXPECT_NE(-1, cap_get_ambient(caps[0])); 156 151 } 157 152 158 153 /* We cannot put such helpers in a library because of kselftest_harness.h . */ ··· 169 158 _init_caps(_metadata, true); 170 159 } 171 160 172 - static void _effective_cap(struct __test_metadata *const _metadata, 173 - const cap_value_t caps, const cap_flag_value_t value) 161 + static void _change_cap(struct __test_metadata *const _metadata, 162 + const cap_flag_t flag, const cap_value_t cap, 163 + const cap_flag_value_t value) 174 164 { 175 165 cap_t cap_p; 176 166 ··· 180 168 { 181 169 TH_LOG("Failed to cap_get_proc: %s", strerror(errno)); 182 170 } 183 - EXPECT_NE(-1, cap_set_flag(cap_p, CAP_EFFECTIVE, 1, &caps, value)) 171 + EXPECT_NE(-1, cap_set_flag(cap_p, flag, 1, &cap, value)) 184 172 { 185 173 TH_LOG("Failed to cap_set_flag: %s", strerror(errno)); 186 174 } ··· 195 183 } 196 184 197 185 static void __maybe_unused set_cap(struct __test_metadata *const _metadata, 198 - const cap_value_t caps) 186 + const cap_value_t cap) 199 187 { 200 - _effective_cap(_metadata, caps, CAP_SET); 188 + _change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_SET); 201 189 } 202 190 203 191 static void __maybe_unused clear_cap(struct __test_metadata *const _metadata, 204 - const cap_value_t caps) 192 + const cap_value_t cap) 205 193 { 206 - _effective_cap(_metadata, caps, CAP_CLEAR); 194 + _change_cap(_metadata, CAP_EFFECTIVE, cap, CAP_CLEAR); 195 + } 196 + 197 + static void __maybe_unused 198 + set_ambient_cap(struct __test_metadata *const _metadata, const cap_value_t cap) 199 + { 200 + _change_cap(_metadata, CAP_INHERITABLE, cap, CAP_SET); 201 + 202 + EXPECT_NE(-1, cap_set_ambient(cap, CAP_SET)) 203 + { 204 + TH_LOG("Failed to set ambient capability %d: %s", cap, 205 + strerror(errno)); 206 + } 207 + } 208 + 209 + static void __maybe_unused clear_ambient_cap( 210 + struct __test_metadata *const _metadata, const cap_value_t cap) 211 + { 212 + EXPECT_EQ(1, cap_get_ambient(cap)); 213 + _change_cap(_metadata, CAP_INHERITABLE, cap, CAP_CLEAR); 214 + EXPECT_EQ(0, cap_get_ambient(cap)); 207 215 } 208 216 209 217 /* Receives an FD from a UNIX socket. Returns the received FD, or -errno. */
+4 -1
tools/testing/selftests/landlock/net_test.c
··· 107 107 { 108 108 set_cap(_metadata, CAP_SYS_ADMIN); 109 109 ASSERT_EQ(0, unshare(CLONE_NEWNET)); 110 - ASSERT_EQ(0, system("ip link set dev lo up")); 111 110 clear_cap(_metadata, CAP_SYS_ADMIN); 111 + 112 + set_ambient_cap(_metadata, CAP_NET_ADMIN); 113 + ASSERT_EQ(0, system("ip link set dev lo up")); 114 + clear_ambient_cap(_metadata, CAP_NET_ADMIN); 112 115 } 113 116 114 117 static bool is_restricted(const struct protocol_variant *const prot,