1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 4 /* 5 * Copyright (C) 2020 Google LLC. 6 */ 7 8 #include <test_progs.h> 9 #include <sched.h> 10 #include <signal.h> 11 #include <string.h> 12 #include <sys/stat.h> 13 #include <sys/syscall.h> 14 #include <sys/wait.h> 15 #include <unistd.h> 16 17 #include <linux/mount.h> 18 19 #include "lsm.skel.h" 20 #include "lsm_tailcall.skel.h" 21 22 char *CMD_ARGS[] = {"true", NULL}; 23 24 enum { 25 INODE_IDMAP_ALL = (1U << 6) - 1, 26 }; 27 28 int exec_cmd(int *monitored_pid) 29 { 30 int child_pid, child_status; 31 32 child_pid = fork(); 33 if (child_pid == 0) { 34 *monitored_pid = getpid(); 35 execvp(CMD_ARGS[0], CMD_ARGS); 36 return -EINVAL; 37 } else if (child_pid > 0) { 38 waitpid(child_pid, &child_status, 0); 39 return child_status; 40 } 41 42 return -EINVAL; 43 } 44 45 static ssize_t write_nointr(int fd, const void *buf, size_t count) 46 { 47 ssize_t ret; 48 49 do { 50 ret = write(fd, buf, count); 51 } while (ret < 0 && errno == EINTR); 52 53 return ret; 54 } 55 56 static int write_file(const char *path, const char *value) 57 { 58 size_t len = strlen(value); 59 int fd, saved_errno = 0; 60 ssize_t ret; 61 62 fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW); 63 if (fd < 0) 64 return -1; 65 66 ret = write_nointr(fd, value, len); 67 if (ret < 0) 68 saved_errno = errno; 69 else if ((size_t)ret != len) 70 saved_errno = EIO; 71 close(fd); 72 if (saved_errno) { 73 errno = saved_errno; 74 return -1; 75 } 76 return 0; 77 } 78 79 static int write_userns_file(pid_t pid, const char *name, const char *value) 80 { 81 char path[64]; 82 int len; 83 84 len = snprintf(path, sizeof(path), "/proc/%d/%s", pid, name); 85 if (len < 0 || (size_t)len >= sizeof(path)) { 86 errno = EOVERFLOW; 87 return -1; 88 } 89 90 return write_file(path, value); 91 } 92 93 static int create_userns_fd(void) 94 { 95 char path[64]; 96 pid_t pid, waited; 97 int fd = -1, len, saved_errno, status; 98 99 pid = fork(); 100 if (pid < 0) 101 return -1; 102 if (pid == 0) { 103 if (unshare(CLONE_NEWUSER)) 104 _exit(1); 105 raise(SIGSTOP); 106 _exit(0); 107 } 108 109 do { 110 waited = waitpid(pid, &status, WUNTRACED); 111 } while (waited < 0 && errno == EINTR); 112 if (waited != pid) 113 goto out; 114 if (!WIFSTOPPED(status)) { 115 pid = -1; 116 goto out; 117 } 118 119 /* A one-entry map is identity for root but remains distinct from nop_mnt_idmap. */ 120 if (write_userns_file(pid, "setgroups", "deny") && errno != ENOENT) 121 goto out; 122 if (write_userns_file(pid, "uid_map", "0 0 1") || 123 write_userns_file(pid, "gid_map", "0 0 1")) 124 goto out; 125 126 len = snprintf(path, sizeof(path), "/proc/%d/ns/user", pid); 127 if (len < 0 || (size_t)len >= sizeof(path)) { 128 errno = EOVERFLOW; 129 goto out; 130 } 131 fd = open(path, O_RDONLY | O_CLOEXEC); 132 133 out: 134 saved_errno = errno; 135 if (pid > 0) { 136 kill(pid, SIGKILL); 137 do { 138 waited = waitpid(pid, NULL, 0); 139 } while (waited < 0 && errno == EINTR); 140 } 141 errno = saved_errno; 142 return fd; 143 } 144 145 static int create_idmapped_tmpfs(void) 146 { 147 struct mount_attr attr = { 148 .attr_set = MOUNT_ATTR_IDMAP, 149 }; 150 int fsfd = -1, mntfd = -1, saved_errno, userns_fd = -1; 151 152 userns_fd = create_userns_fd(); 153 if (userns_fd < 0) 154 goto out; 155 156 /* A detached tmpfs avoids relying on the host test directory supporting idmaps. */ 157 fsfd = syscall(__NR_fsopen, "tmpfs", FSOPEN_CLOEXEC); 158 if (fsfd < 0) 159 goto out; 160 if (syscall(__NR_fsconfig, fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0)) 161 goto out; 162 163 mntfd = syscall(__NR_fsmount, fsfd, FSMOUNT_CLOEXEC, 0); 164 if (mntfd < 0) 165 goto out; 166 167 attr.userns_fd = userns_fd; 168 if (syscall(__NR_mount_setattr, mntfd, "", AT_EMPTY_PATH, &attr, 169 sizeof(attr))) { 170 close(mntfd); 171 mntfd = -1; 172 } 173 174 out: 175 saved_errno = errno; 176 if (fsfd >= 0) 177 close(fsfd); 178 if (userns_fd >= 0) 179 close(userns_fd); 180 errno = saved_errno; 181 return mntfd; 182 } 183 184 static int exercise_inode_idmap_hooks(int dirfd) 185 { 186 int fd = -1, ret = -1; 187 188 fd = openat(dirfd, "file", O_CREAT | O_EXCL | O_WRONLY | O_CLOEXEC, 189 0600); 190 if (!ASSERT_GE(fd, 0, "create")) 191 goto out; 192 close(fd); 193 fd = -1; 194 195 if (!ASSERT_OK(mkdirat(dirfd, "dir", 0700), "mkdir")) 196 goto out; 197 if (!ASSERT_OK(symlinkat("target", dirfd, "symlink"), "symlink")) 198 goto out; 199 if (!ASSERT_OK(linkat(dirfd, "file", dirfd, "link", 0), "link")) 200 goto out; 201 if (!ASSERT_OK(mkfifoat(dirfd, "fifo", 0600), "mknod")) 202 goto out; 203 204 ret = 0; 205 out: 206 if (fd >= 0) 207 close(fd); 208 unlinkat(dirfd, "link", 0); 209 unlinkat(dirfd, "fifo", 0); 210 unlinkat(dirfd, "symlink", 0); 211 unlinkat(dirfd, "file", 0); 212 unlinkat(dirfd, "dir", AT_REMOVEDIR); 213 return ret; 214 } 215 216 static int test_lsm_inode_idmap(struct lsm *skel) 217 { 218 char tmpdir[] = "/var/tmp/test_lsm_idmap.XXXXXX"; 219 __u32 expected = INODE_IDMAP_ALL; 220 int dirfd = -1, idmapped_dirfd = -1; 221 int ret = -1; 222 223 if (!ASSERT_OK_PTR(mkdtemp(tmpdir), "mkdtemp")) 224 return -1; 225 226 dirfd = open(tmpdir, O_RDONLY | O_DIRECTORY | O_CLOEXEC); 227 if (!ASSERT_GE(dirfd, 0, "open_tmpdir")) 228 goto out; 229 230 idmapped_dirfd = create_idmapped_tmpfs(); 231 if (!ASSERT_GE(idmapped_dirfd, 0, "create_idmapped_tmpfs")) 232 goto out; 233 234 skel->bss->inode_identity_idmap_seen = 0; 235 skel->bss->inode_idmapped_mount_seen = 0; 236 237 if (!ASSERT_OK(exercise_inode_idmap_hooks(dirfd), "identity_idmap")) 238 goto out; 239 if (!ASSERT_OK(exercise_inode_idmap_hooks(idmapped_dirfd), 240 "idmapped_mount")) 241 goto out; 242 243 if (!ASSERT_EQ(skel->bss->inode_identity_idmap_seen, expected, 244 "inode_identity_idmap_seen")) 245 goto out; 246 ret = ASSERT_EQ(skel->bss->inode_idmapped_mount_seen, expected, 247 "inode_idmapped_mount_seen") ? 0 : -1; 248 249 out: 250 if (idmapped_dirfd >= 0) 251 close(idmapped_dirfd); 252 if (dirfd >= 0) 253 close(dirfd); 254 rmdir(tmpdir); 255 return ret; 256 } 257 258 static int test_lsm(struct lsm *skel) 259 { 260 struct bpf_link *link; 261 int buf = 1234; 262 int err; 263 264 err = lsm__attach(skel); 265 if (!ASSERT_OK(err, "attach")) 266 return err; 267 268 /* Check that already linked program can't be attached again. */ 269 link = bpf_program__attach(skel->progs.test_int_hook); 270 if (!ASSERT_ERR_PTR(link, "attach_link")) 271 return -1; 272 273 err = exec_cmd(&skel->bss->monitored_pid); 274 if (!ASSERT_OK(err, "exec_cmd")) 275 return err; 276 277 ASSERT_EQ(skel->bss->bprm_count, 1, "bprm_count"); 278 279 skel->bss->monitored_pid = getpid(); 280 281 err = test_lsm_inode_idmap(skel); 282 if (!ASSERT_OK(err, "test_lsm_inode_idmap")) 283 return err; 284 285 err = stack_mprotect(); 286 if (!ASSERT_EQ(err, -1, "stack_mprotect") || 287 !ASSERT_EQ(errno, EPERM, "stack_mprotect")) 288 return err; 289 290 ASSERT_EQ(skel->bss->mprotect_count, 1, "mprotect_count"); 291 292 syscall(__NR_setdomainname, &buf, -2L); 293 syscall(__NR_setdomainname, 0, -3L); 294 syscall(__NR_setdomainname, ~0L, -4L); 295 296 ASSERT_EQ(skel->bss->copy_test, 3, "copy_test"); 297 298 lsm__detach(skel); 299 300 skel->bss->copy_test = 0; 301 skel->bss->bprm_count = 0; 302 skel->bss->mprotect_count = 0; 303 skel->bss->inode_identity_idmap_seen = 0; 304 skel->bss->inode_idmapped_mount_seen = 0; 305 return 0; 306 } 307 308 static void test_lsm_basic(void) 309 { 310 struct lsm *skel = NULL; 311 int err; 312 313 skel = lsm__open_and_load(); 314 if (!ASSERT_OK_PTR(skel, "lsm_skel_load")) 315 goto close_prog; 316 317 err = test_lsm(skel); 318 if (!ASSERT_OK(err, "test_lsm_first_attach")) 319 goto close_prog; 320 321 err = test_lsm(skel); 322 ASSERT_OK(err, "test_lsm_second_attach"); 323 324 close_prog: 325 lsm__destroy(skel); 326 } 327 328 static void test_lsm_tailcall(void) 329 { 330 struct lsm_tailcall *skel = NULL; 331 int map_fd, prog_fd; 332 int err, key; 333 334 skel = lsm_tailcall__open_and_load(); 335 if (!ASSERT_OK_PTR(skel, "lsm_tailcall__skel_load")) 336 goto close_prog; 337 338 map_fd = bpf_map__fd(skel->maps.jmp_table); 339 if (CHECK_FAIL(map_fd < 0)) 340 goto close_prog; 341 342 prog_fd = bpf_program__fd(skel->progs.lsm_file_permission_prog); 343 if (CHECK_FAIL(prog_fd < 0)) 344 goto close_prog; 345 346 key = 0; 347 err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); 348 if (CHECK_FAIL(!err)) 349 goto close_prog; 350 351 prog_fd = bpf_program__fd(skel->progs.lsm_kernfs_init_security_prog); 352 if (CHECK_FAIL(prog_fd < 0)) 353 goto close_prog; 354 355 err = bpf_map_update_elem(map_fd, &key, &prog_fd, BPF_ANY); 356 if (CHECK_FAIL(err)) 357 goto close_prog; 358 359 close_prog: 360 lsm_tailcall__destroy(skel); 361 } 362 363 void test_test_lsm(void) 364 { 365 if (test__start_subtest("lsm_basic")) 366 test_lsm_basic(); 367 if (test__start_subtest("lsm_tailcall")) 368 test_lsm_tailcall(); 369 } 370