Skip to content

Commit 1004714

Browse files
committed
Merge patch series "tighten nstree visibility checks"
Christian Brauner <[email protected]> says: Listing various namespaces is currently only scoped on owning namespace. We can make this more fine-grained so that we scope visibility even tighter. To make it possible to change behavior restrict visibility for now. This shouldn't be a big deal as there aren't actual large users out there and paves the way to make this even cleaner in the future. * patches from https://patch.msgid.link/[email protected]: selftests: fix mntns iteration selftests nstree: tighten permission checks for listing nsfs: tighten permission checks for handle opening nsfs: tighten permission checks for ns iteration ioctls Link: https://patch.msgid.link/[email protected] Signed-off-by: Christian Brauner <[email protected]>
2 parents a0b4c7a + 4c7b2ec commit 1004714

5 files changed

Lines changed: 41 additions & 36 deletions

File tree

fs/nsfs.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,17 @@ static bool nsfs_ioctl_valid(unsigned int cmd)
199199
return false;
200200
}
201201

202+
static bool may_use_nsfs_ioctl(unsigned int cmd)
203+
{
204+
switch (_IOC_NR(cmd)) {
205+
case _IOC_NR(NS_MNT_GET_NEXT):
206+
fallthrough;
207+
case _IOC_NR(NS_MNT_GET_PREV):
208+
return may_see_all_namespaces();
209+
}
210+
return true;
211+
}
212+
202213
static long ns_ioctl(struct file *filp, unsigned int ioctl,
203214
unsigned long arg)
204215
{
@@ -214,6 +225,8 @@ static long ns_ioctl(struct file *filp, unsigned int ioctl,
214225

215226
if (!nsfs_ioctl_valid(ioctl))
216227
return -ENOIOCTLCMD;
228+
if (!may_use_nsfs_ioctl(ioctl))
229+
return -EPERM;
217230

218231
ns = get_proc_ns(file_inode(filp));
219232
switch (ioctl) {
@@ -614,7 +627,7 @@ static struct dentry *nsfs_fh_to_dentry(struct super_block *sb, struct fid *fh,
614627
return ERR_PTR(-EOPNOTSUPP);
615628
}
616629

617-
if (owning_ns && !ns_capable(owning_ns, CAP_SYS_ADMIN)) {
630+
if (owning_ns && !may_see_all_namespaces()) {
618631
ns->ops->put(ns);
619632
return ERR_PTR(-EPERM);
620633
}

include/linux/ns_common.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ static __always_inline bool is_ns_init_id(const struct ns_common *ns)
5555

5656
#define ns_common_free(__ns) __ns_common_free(to_ns_common((__ns)))
5757

58+
bool may_see_all_namespaces(void);
59+
5860
static __always_inline __must_check int __ns_ref_active_read(const struct ns_common *ns)
5961
{
6062
return atomic_read(&ns->__ns_ref_active);

kernel/nscommon.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,3 +309,9 @@ void __ns_ref_active_get(struct ns_common *ns)
309309
return;
310310
}
311311
}
312+
313+
bool may_see_all_namespaces(void)
314+
{
315+
return (task_active_pid_ns(current) == &init_pid_ns) &&
316+
ns_capable_noaudit(init_pid_ns.user_ns, CAP_SYS_ADMIN);
317+
}

kernel/nstree.c

Lines changed: 4 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -515,32 +515,11 @@ static inline bool __must_check ns_requested(const struct klistns *kls,
515515
static inline bool __must_check may_list_ns(const struct klistns *kls,
516516
struct ns_common *ns)
517517
{
518-
if (kls->user_ns) {
519-
if (kls->userns_capable)
520-
return true;
521-
} else {
522-
struct ns_common *owner;
523-
struct user_namespace *user_ns;
524-
525-
owner = ns_owner(ns);
526-
if (owner)
527-
user_ns = to_user_ns(owner);
528-
else
529-
user_ns = &init_user_ns;
530-
if (ns_capable_noaudit(user_ns, CAP_SYS_ADMIN))
531-
return true;
532-
}
533-
534-
if (is_current_namespace(ns))
518+
if (kls->user_ns && kls->userns_capable)
535519
return true;
536-
537-
if (ns->ns_type != CLONE_NEWUSER)
538-
return false;
539-
540-
if (ns_capable_noaudit(to_user_ns(ns), CAP_SYS_ADMIN))
520+
if (is_current_namespace(ns))
541521
return true;
542-
543-
return false;
522+
return may_see_all_namespaces();
544523
}
545524

546525
static inline void ns_put(struct ns_common *ns)
@@ -600,7 +579,7 @@ static ssize_t do_listns_userns(struct klistns *kls)
600579

601580
ret = 0;
602581
head = &to_ns_common(kls->user_ns)->ns_owner_root.ns_list_head;
603-
kls->userns_capable = ns_capable_noaudit(kls->user_ns, CAP_SYS_ADMIN);
582+
kls->userns_capable = may_see_all_namespaces();
604583

605584
rcu_read_lock();
606585

tools/testing/selftests/filesystems/nsfs/iterate_mntns.c

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,17 +37,20 @@ FIXTURE(iterate_mount_namespaces) {
3737
__u64 mnt_ns_id[MNT_NS_COUNT];
3838
};
3939

40+
static inline bool mntns_in_list(__u64 *mnt_ns_id, struct mnt_ns_info *info)
41+
{
42+
for (int i = 0; i < MNT_NS_COUNT; i++) {
43+
if (mnt_ns_id[i] == info->mnt_ns_id)
44+
return true;
45+
}
46+
return false;
47+
}
48+
4049
FIXTURE_SETUP(iterate_mount_namespaces)
4150
{
4251
for (int i = 0; i < MNT_NS_COUNT; i++)
4352
self->fd_mnt_ns[i] = -EBADF;
4453

45-
/*
46-
* Creating a new user namespace let's us guarantee that we only see
47-
* mount namespaces that we did actually create.
48-
*/
49-
ASSERT_EQ(unshare(CLONE_NEWUSER), 0);
50-
5154
for (int i = 0; i < MNT_NS_COUNT; i++) {
5255
struct mnt_ns_info info = {};
5356

@@ -75,13 +78,15 @@ TEST_F(iterate_mount_namespaces, iterate_all_forward)
7578
fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[0], F_DUPFD_CLOEXEC);
7679
ASSERT_GE(fd_mnt_ns_cur, 0);
7780

78-
for (;; count++) {
81+
for (;;) {
7982
struct mnt_ns_info info = {};
8083
int fd_mnt_ns_next;
8184

8285
fd_mnt_ns_next = ioctl(fd_mnt_ns_cur, NS_MNT_GET_NEXT, &info);
8386
if (fd_mnt_ns_next < 0 && errno == ENOENT)
8487
break;
88+
if (mntns_in_list(self->mnt_ns_id, &info))
89+
count++;
8590
ASSERT_GE(fd_mnt_ns_next, 0);
8691
ASSERT_EQ(close(fd_mnt_ns_cur), 0);
8792
fd_mnt_ns_cur = fd_mnt_ns_next;
@@ -96,13 +101,15 @@ TEST_F(iterate_mount_namespaces, iterate_all_backwards)
96101
fd_mnt_ns_cur = fcntl(self->fd_mnt_ns[MNT_NS_LAST_INDEX], F_DUPFD_CLOEXEC);
97102
ASSERT_GE(fd_mnt_ns_cur, 0);
98103

99-
for (;; count++) {
104+
for (;;) {
100105
struct mnt_ns_info info = {};
101106
int fd_mnt_ns_prev;
102107

103108
fd_mnt_ns_prev = ioctl(fd_mnt_ns_cur, NS_MNT_GET_PREV, &info);
104109
if (fd_mnt_ns_prev < 0 && errno == ENOENT)
105110
break;
111+
if (mntns_in_list(self->mnt_ns_id, &info))
112+
count++;
106113
ASSERT_GE(fd_mnt_ns_prev, 0);
107114
ASSERT_EQ(close(fd_mnt_ns_cur), 0);
108115
fd_mnt_ns_cur = fd_mnt_ns_prev;
@@ -125,7 +132,6 @@ TEST_F(iterate_mount_namespaces, iterate_forward)
125132
ASSERT_GE(fd_mnt_ns_next, 0);
126133
ASSERT_EQ(close(fd_mnt_ns_cur), 0);
127134
fd_mnt_ns_cur = fd_mnt_ns_next;
128-
ASSERT_EQ(info.mnt_ns_id, self->mnt_ns_id[i]);
129135
}
130136
}
131137

@@ -144,7 +150,6 @@ TEST_F(iterate_mount_namespaces, iterate_backward)
144150
ASSERT_GE(fd_mnt_ns_prev, 0);
145151
ASSERT_EQ(close(fd_mnt_ns_cur), 0);
146152
fd_mnt_ns_cur = fd_mnt_ns_prev;
147-
ASSERT_EQ(info.mnt_ns_id, self->mnt_ns_id[i]);
148153
}
149154
}
150155

0 commit comments

Comments
 (0)