Skip to content

Commit 7e66b6c

Browse files
shailend-ggvisor-bot
authored andcommitted
tun: Check for cap in the netns-owning userns when creating tun
Instead of the ioctl-calling task's own userns. Analogous to how Linux does a `ns_capable(net->user_ns, CAP_NET_ADMIN)`. PiperOrigin-RevId: 842903973
1 parent 4485bf5 commit 7e66b6c

File tree

4 files changed

+32
-1
lines changed

4 files changed

+32
-1
lines changed

pkg/sentry/devices/tundev/tundev.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ func (fd *tunFD) Ioctl(ctx context.Context, uio usermem.IO, sysno uintptr, args
8383

8484
switch request {
8585
case linux.TUNSETIFF:
86-
if !t.HasCapability(linux.CAP_NET_ADMIN) {
86+
if !t.HasCapabilityIn(linux.CAP_NET_ADMIN, t.NetworkNamespace().UserNamespace()) {
8787
return 0, linuxerr.EPERM
8888
}
8989
stack, ok := t.NetworkContext().(*netstack.Stack)

test/syscalls/linux/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4280,6 +4280,8 @@ cc_binary(
42804280
"//test/util:capability_util",
42814281
"//test/util:file_descriptor",
42824282
"//test/util:fs_util",
4283+
"//test/util:logging",
4284+
"//test/util:multiprocess_util",
42834285
"//test/util:posix_error",
42844286
"//test/util:socket_util",
42854287
"//test/util:test_main",

test/syscalls/linux/network_namespace.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "test/syscalls/linux/ip_socket_test_util.h"
1919
#include "test/util/capability_util.h"
2020
#include "test/util/file_descriptor.h"
21+
#include "test/util/linux_capability_util.h"
2122
#include "test/util/temp_path.h"
2223
#include "test/util/test_util.h"
2324
#include "test/util/thread_util.h"

test/syscalls/linux/tuntap.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@
3838
#include "test/util/capability_util.h"
3939
#include "test/util/file_descriptor.h"
4040
#include "test/util/fs_util.h"
41+
#include "test/util/linux_capability_util.h"
42+
#include "test/util/logging.h"
43+
#include "test/util/multiprocess_util.h"
4144
#include "test/util/posix_error.h"
4245
#include "test/util/socket_util.h"
4346
#include "test/util/test_util.h"
@@ -218,6 +221,31 @@ TEST_F(TuntapTest, CreateInterfaceNoCap) {
218221
EXPECT_THAT(ioctl(fd.get(), TUNSETIFF, &ifr), SyscallFailsWithErrno(EPERM));
219222
}
220223

224+
TEST_F(TuntapTest, CreateInterfaceNoCapCantCheatWithUnshare) {
225+
AutoCapability cap(CAP_NET_ADMIN, /*has_cap=*/false);
226+
227+
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(Open(kDevNetTun, O_RDWR));
228+
int fd_raw = fd.get();
229+
struct ifreq ifr = {};
230+
ifr.ifr_flags |= IFF_TUN;
231+
strncpy(ifr.ifr_name, kTunName, IFNAMSIZ);
232+
233+
// Fork to avoid changing the user namespace of the original test process.
234+
EXPECT_THAT(
235+
InForkedProcess([&] {
236+
// Fails with EPERM because we don't have CAP_NET_ADMIN.
237+
TEST_CHECK_ERRNO(syscall(SYS_ioctl, fd_raw, TUNSETIFF, &ifr), EPERM);
238+
239+
// Enter a new user namespace in which we'd have all caps.
240+
TEST_PCHECK(syscall(SYS_unshare, CLONE_NEWUSER) == 0);
241+
// Still fails with EPERM because we still don't have CAP_NET_ADMIN in
242+
// the owning namespace of the netns, even if we do in the new userns.
243+
TEST_CHECK_ERRNO(syscall(SYS_ioctl, fd_raw, TUNSETIFF, &ifr), EPERM);
244+
_exit(0);
245+
}),
246+
IsPosixErrorOkAndHolds(0));
247+
}
248+
221249
TEST_F(TuntapTest, CreateFixedNameInterface) {
222250
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
223251

0 commit comments

Comments
 (0)