1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <sched.h> 7 #include <stdio.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <linux/nsfs.h> 11 #include <sys/capability.h> 12 #include <sys/ioctl.h> 13 #include <sys/prctl.h> 14 #include <sys/stat.h> 15 #include <sys/syscall.h> 16 #include <sys/types.h> 17 #include <sys/wait.h> 18 #include <unistd.h> 19 #include "../kselftest_harness.h" 20 #include "../filesystems/utils.h" 21 #include "wrappers.h" 22 23 /* 24 * Test that unprivileged users can only see namespaces they're currently in. 25 * Create a namespace, drop privileges, verify we can only see our own namespaces. 26 */ 27 TEST(listns_unprivileged_current_only) 28 { 29 struct ns_id_req req = { 30 .size = sizeof(req), 31 .spare = 0, 32 .ns_id = 0, 33 .ns_type = CLONE_NEWNET, 34 .spare2 = 0, 35 .user_ns_id = 0, 36 }; 37 __u64 ns_ids[100]; 38 ssize_t ret; 39 int pipefd[2]; 40 pid_t pid; 41 int status; 42 bool found_ours; 43 int unexpected_count; 44 45 ASSERT_EQ(pipe(pipefd), 0); 46 47 pid = fork(); 48 ASSERT_GE(pid, 0); 49 50 if (pid == 0) { 51 int fd; 52 __u64 our_netns_id; 53 bool found_ours; 54 int unexpected_count; 55 56 close(pipefd[0]); 57 58 /* Create user namespace to be unprivileged */ 59 if (setup_userns() < 0) { 60 close(pipefd[1]); 61 exit(1); 62 } 63 64 /* Create a network namespace */ 65 if (unshare(CLONE_NEWNET) < 0) { 66 close(pipefd[1]); 67 exit(1); 68 } 69 70 /* Get our network namespace ID */ 71 fd = open("/proc/self/ns/net", O_RDONLY); 72 if (fd < 0) { 73 close(pipefd[1]); 74 exit(1); 75 } 76 77 if (ioctl(fd, NS_GET_ID, &our_netns_id) < 0) { 78 close(fd); 79 close(pipefd[1]); 80 exit(1); 81 } 82 close(fd); 83 84 /* Now we're unprivileged - list all network namespaces */ 85 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 86 if (ret < 0) { 87 close(pipefd[1]); 88 exit(1); 89 } 90 91 /* We should only see our own network namespace */ 92 found_ours = false; 93 unexpected_count = 0; 94 95 for (ssize_t i = 0; i < ret; i++) { 96 if (ns_ids[i] == our_netns_id) { 97 found_ours = true; 98 } else { 99 /* This is either init_net (which we can see) or unexpected */ 100 unexpected_count++; 101 } 102 } 103 104 /* Send results to parent */ 105 write(pipefd[1], &found_ours, sizeof(found_ours)); 106 write(pipefd[1], &unexpected_count, sizeof(unexpected_count)); 107 close(pipefd[1]); 108 exit(0); 109 } 110 111 /* Parent */ 112 close(pipefd[1]); 113 114 found_ours = false; 115 unexpected_count = 0; 116 read(pipefd[0], &found_ours, sizeof(found_ours)); 117 read(pipefd[0], &unexpected_count, sizeof(unexpected_count)); 118 close(pipefd[0]); 119 120 waitpid(pid, &status, 0); 121 ASSERT_TRUE(WIFEXITED(status)); 122 ASSERT_EQ(WEXITSTATUS(status), 0); 123 124 /* Child should have seen its own namespace */ 125 ASSERT_TRUE(found_ours); 126 127 TH_LOG("Unprivileged child saw its own namespace, plus %d others (likely init_net)", 128 unexpected_count); 129 } 130 131 /* 132 * Test that users with CAP_SYS_ADMIN in a user namespace can see 133 * all namespaces owned by that user namespace. 134 */ 135 TEST(listns_cap_sys_admin_in_userns) 136 { 137 struct ns_id_req req = { 138 .size = sizeof(req), 139 .spare = 0, 140 .ns_id = 0, 141 .ns_type = 0, /* All types */ 142 .spare2 = 0, 143 .user_ns_id = 0, /* Will be set to our created user namespace */ 144 }; 145 __u64 ns_ids[100]; 146 int pipefd[2]; 147 pid_t pid; 148 int status; 149 bool success; 150 ssize_t count; 151 152 ASSERT_EQ(pipe(pipefd), 0); 153 154 pid = fork(); 155 ASSERT_GE(pid, 0); 156 157 if (pid == 0) { 158 int fd; 159 __u64 userns_id; 160 ssize_t ret; 161 int min_expected; 162 bool success; 163 164 close(pipefd[0]); 165 166 /* Create user namespace - we'll have CAP_SYS_ADMIN in it */ 167 if (setup_userns() < 0) { 168 close(pipefd[1]); 169 exit(1); 170 } 171 172 /* Get the user namespace ID */ 173 fd = open("/proc/self/ns/user", O_RDONLY); 174 if (fd < 0) { 175 close(pipefd[1]); 176 exit(1); 177 } 178 179 if (ioctl(fd, NS_GET_ID, &userns_id) < 0) { 180 close(fd); 181 close(pipefd[1]); 182 exit(1); 183 } 184 close(fd); 185 186 /* Create several namespaces owned by this user namespace */ 187 unshare(CLONE_NEWNET); 188 unshare(CLONE_NEWUTS); 189 unshare(CLONE_NEWIPC); 190 191 /* List namespaces owned by our user namespace */ 192 req.user_ns_id = userns_id; 193 ret = sys_listns(&req, ns_ids, ARRAY_SIZE(ns_ids), 0); 194 if (ret < 0) { 195 close(pipefd[1]); 196 exit(1); 197 } 198 199 /* 200 * We have CAP_SYS_ADMIN in this user namespace, 201 * so we should see all namespaces owned by it. 202 * That includes: net, uts, ipc, and the user namespace itself. 203 */ 204 min_expected = 4; 205 success = (ret >= min_expected); 206 207 write(pipefd[1], &success, sizeof(success)); 208 write(pipefd[1], &ret, sizeof(ret)); 209 close(pipefd[1]); 210 exit(0); 211 } 212 213 /* Parent */ 214 close(pipefd[1]); 215 216 success = false; 217 count = 0; 218 read(pipefd[0], &success, sizeof(success)); 219 read(pipefd[0], &count, sizeof(count)); 220 close(pipefd[0]); 221 222 waitpid(pid, &status, 0); 223 ASSERT_TRUE(WIFEXITED(status)); 224 ASSERT_EQ(WEXITSTATUS(status), 0); 225 226 ASSERT_TRUE(success); 227 TH_LOG("User with CAP_SYS_ADMIN saw %zd namespaces owned by their user namespace", 228 count); 229 } 230 231 TEST_HARNESS_MAIN 232