1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2023 Meta Platforms, Inc. and affiliates. */ 3 #define _GNU_SOURCE 4 #include <test_progs.h> 5 #include <bpf/btf.h> 6 #include "cap_helpers.h" 7 #include <fcntl.h> 8 #include <sched.h> 9 #include <signal.h> 10 #include <unistd.h> 11 #include <linux/filter.h> 12 #include <linux/unistd.h> 13 #include <linux/mount.h> 14 #include <sys/socket.h> 15 #include <sys/stat.h> 16 #include <sys/syscall.h> 17 #include <sys/un.h> 18 #include "priv_map.skel.h" 19 #include "priv_prog.skel.h" 20 #include "dummy_st_ops_success.skel.h" 21 #include "token_lsm.skel.h" 22 23 static inline int sys_mount(const char *dev_name, const char *dir_name, 24 const char *type, unsigned long flags, 25 const void *data) 26 { 27 return syscall(__NR_mount, dev_name, dir_name, type, flags, data); 28 } 29 30 static inline int sys_fsopen(const char *fsname, unsigned flags) 31 { 32 return syscall(__NR_fsopen, fsname, flags); 33 } 34 35 static inline int sys_fspick(int dfd, const char *path, unsigned flags) 36 { 37 return syscall(__NR_fspick, dfd, path, flags); 38 } 39 40 static inline int sys_fsconfig(int fs_fd, unsigned cmd, const char *key, const void *val, int aux) 41 { 42 return syscall(__NR_fsconfig, fs_fd, cmd, key, val, aux); 43 } 44 45 static inline int sys_fsmount(int fs_fd, unsigned flags, unsigned ms_flags) 46 { 47 return syscall(__NR_fsmount, fs_fd, flags, ms_flags); 48 } 49 50 static inline int sys_move_mount(int from_dfd, const char *from_path, 51 int to_dfd, const char *to_path, 52 unsigned flags) 53 { 54 return syscall(__NR_move_mount, from_dfd, from_path, to_dfd, to_path, flags); 55 } 56 57 static int drop_priv_caps(__u64 *old_caps) 58 { 59 return cap_disable_effective((1ULL << CAP_BPF) | 60 (1ULL << CAP_PERFMON) | 61 (1ULL << CAP_NET_ADMIN) | 62 (1ULL << CAP_SYS_ADMIN), old_caps); 63 } 64 65 static int restore_priv_caps(__u64 old_caps) 66 { 67 return cap_enable_effective(old_caps, NULL); 68 } 69 70 static int set_delegate_mask(int fs_fd, const char *key, __u64 mask, const char *mask_str) 71 { 72 char buf[32]; 73 int err; 74 75 if (!mask_str) { 76 if (mask == ~0ULL) { 77 mask_str = "any"; 78 } else { 79 snprintf(buf, sizeof(buf), "0x%llx", (unsigned long long)mask); 80 mask_str = buf; 81 } 82 } 83 84 err = sys_fsconfig(fs_fd, FSCONFIG_SET_STRING, key, 85 mask_str, 0); 86 if (err < 0) 87 err = -errno; 88 return err; 89 } 90 91 #define zclose(fd) do { if (fd >= 0) close(fd); fd = -1; } while (0) 92 93 struct bpffs_opts { 94 __u64 cmds; 95 __u64 maps; 96 __u64 progs; 97 __u64 attachs; 98 const char *cmds_str; 99 const char *maps_str; 100 const char *progs_str; 101 const char *attachs_str; 102 }; 103 104 static int create_bpffs_fd(void) 105 { 106 int fs_fd; 107 108 /* create VFS context */ 109 fs_fd = sys_fsopen("bpf", 0); 110 ASSERT_GE(fs_fd, 0, "fs_fd"); 111 112 return fs_fd; 113 } 114 115 static int materialize_bpffs_fd(int fs_fd, struct bpffs_opts *opts) 116 { 117 int mnt_fd, err; 118 119 /* set up token delegation mount options */ 120 err = set_delegate_mask(fs_fd, "delegate_cmds", opts->cmds, opts->cmds_str); 121 if (!ASSERT_OK(err, "fs_cfg_cmds")) 122 return err; 123 err = set_delegate_mask(fs_fd, "delegate_maps", opts->maps, opts->maps_str); 124 if (!ASSERT_OK(err, "fs_cfg_maps")) 125 return err; 126 err = set_delegate_mask(fs_fd, "delegate_progs", opts->progs, opts->progs_str); 127 if (!ASSERT_OK(err, "fs_cfg_progs")) 128 return err; 129 err = set_delegate_mask(fs_fd, "delegate_attachs", opts->attachs, opts->attachs_str); 130 if (!ASSERT_OK(err, "fs_cfg_attachs")) 131 return err; 132 133 /* instantiate FS object */ 134 err = sys_fsconfig(fs_fd, FSCONFIG_CMD_CREATE, NULL, NULL, 0); 135 if (err < 0) 136 return -errno; 137 138 /* create O_PATH fd for detached mount */ 139 mnt_fd = sys_fsmount(fs_fd, 0, 0); 140 if (err < 0) 141 return -errno; 142 143 return mnt_fd; 144 } 145 146 /* send FD over Unix domain (AF_UNIX) socket */ 147 static int sendfd(int sockfd, int fd) 148 { 149 struct msghdr msg = {}; 150 struct cmsghdr *cmsg; 151 int fds[1] = { fd }, err; 152 char iobuf[1]; 153 struct iovec io = { 154 .iov_base = iobuf, 155 .iov_len = sizeof(iobuf), 156 }; 157 union { 158 char buf[CMSG_SPACE(sizeof(fds))]; 159 struct cmsghdr align; 160 } u; 161 162 msg.msg_iov = &io; 163 msg.msg_iovlen = 1; 164 msg.msg_control = u.buf; 165 msg.msg_controllen = sizeof(u.buf); 166 cmsg = CMSG_FIRSTHDR(&msg); 167 cmsg->cmsg_level = SOL_SOCKET; 168 cmsg->cmsg_type = SCM_RIGHTS; 169 cmsg->cmsg_len = CMSG_LEN(sizeof(fds)); 170 memcpy(CMSG_DATA(cmsg), fds, sizeof(fds)); 171 172 err = sendmsg(sockfd, &msg, 0); 173 if (err < 0) 174 err = -errno; 175 if (!ASSERT_EQ(err, 1, "sendmsg")) 176 return -EINVAL; 177 178 return 0; 179 } 180 181 /* receive FD over Unix domain (AF_UNIX) socket */ 182 static int recvfd(int sockfd, int *fd) 183 { 184 struct msghdr msg = {}; 185 struct cmsghdr *cmsg; 186 int fds[1], err; 187 char iobuf[1]; 188 struct iovec io = { 189 .iov_base = iobuf, 190 .iov_len = sizeof(iobuf), 191 }; 192 union { 193 char buf[CMSG_SPACE(sizeof(fds))]; 194 struct cmsghdr align; 195 } u; 196 197 msg.msg_iov = &io; 198 msg.msg_iovlen = 1; 199 msg.msg_control = u.buf; 200 msg.msg_controllen = sizeof(u.buf); 201 202 err = recvmsg(sockfd, &msg, 0); 203 if (err < 0) 204 err = -errno; 205 if (!ASSERT_EQ(err, 1, "recvmsg")) 206 return -EINVAL; 207 208 cmsg = CMSG_FIRSTHDR(&msg); 209 if (!ASSERT_OK_PTR(cmsg, "cmsg_null") || 210 !ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(fds)), "cmsg_len") || 211 !ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET, "cmsg_level") || 212 !ASSERT_EQ(cmsg->cmsg_type, SCM_RIGHTS, "cmsg_type")) 213 return -EINVAL; 214 215 memcpy(fds, CMSG_DATA(cmsg), sizeof(fds)); 216 *fd = fds[0]; 217 218 return 0; 219 } 220 221 static ssize_t write_nointr(int fd, const void *buf, size_t count) 222 { 223 ssize_t ret; 224 225 do { 226 ret = write(fd, buf, count); 227 } while (ret < 0 && errno == EINTR); 228 229 return ret; 230 } 231 232 static int write_file(const char *path, const void *buf, size_t count) 233 { 234 int fd; 235 ssize_t ret; 236 237 fd = open(path, O_WRONLY | O_CLOEXEC | O_NOCTTY | O_NOFOLLOW); 238 if (fd < 0) 239 return -1; 240 241 ret = write_nointr(fd, buf, count); 242 close(fd); 243 if (ret < 0 || (size_t)ret != count) 244 return -1; 245 246 return 0; 247 } 248 249 static int create_and_enter_userns(void) 250 { 251 uid_t uid; 252 gid_t gid; 253 char map[100]; 254 255 uid = getuid(); 256 gid = getgid(); 257 258 if (unshare(CLONE_NEWUSER)) 259 return -1; 260 261 if (write_file("/proc/self/setgroups", "deny", sizeof("deny") - 1) && 262 errno != ENOENT) 263 return -1; 264 265 snprintf(map, sizeof(map), "0 %d 1", uid); 266 if (write_file("/proc/self/uid_map", map, strlen(map))) 267 return -1; 268 269 270 snprintf(map, sizeof(map), "0 %d 1", gid); 271 if (write_file("/proc/self/gid_map", map, strlen(map))) 272 return -1; 273 274 if (setgid(0)) 275 return -1; 276 277 if (setuid(0)) 278 return -1; 279 280 return 0; 281 } 282 283 typedef int (*child_callback_fn)(int bpffs_fd, struct token_lsm *lsm_skel); 284 285 static void child(int sock_fd, struct bpffs_opts *opts, child_callback_fn callback) 286 { 287 int mnt_fd = -1, fs_fd = -1, err = 0, bpffs_fd = -1, token_fd = -1; 288 struct token_lsm *lsm_skel = NULL; 289 290 /* load and attach LSM "policy" before we go into unpriv userns */ 291 lsm_skel = token_lsm__open_and_load(); 292 if (!ASSERT_OK_PTR(lsm_skel, "lsm_skel_load")) { 293 err = -EINVAL; 294 goto cleanup; 295 } 296 lsm_skel->bss->my_pid = getpid(); 297 err = token_lsm__attach(lsm_skel); 298 if (!ASSERT_OK(err, "lsm_skel_attach")) 299 goto cleanup; 300 301 /* setup userns with root mappings */ 302 err = create_and_enter_userns(); 303 if (!ASSERT_OK(err, "create_and_enter_userns")) 304 goto cleanup; 305 306 /* setup mountns to allow creating BPF FS (fsopen("bpf")) from unpriv process */ 307 err = unshare(CLONE_NEWNS); 308 if (!ASSERT_OK(err, "create_mountns")) 309 goto cleanup; 310 311 err = sys_mount(NULL, "/", NULL, MS_REC | MS_PRIVATE, 0); 312 if (!ASSERT_OK(err, "remount_root")) 313 goto cleanup; 314 315 fs_fd = create_bpffs_fd(); 316 if (!ASSERT_GE(fs_fd, 0, "create_bpffs_fd")) { 317 err = -EINVAL; 318 goto cleanup; 319 } 320 321 /* ensure unprivileged child cannot set delegation options */ 322 err = set_delegate_mask(fs_fd, "delegate_cmds", 0x1, NULL); 323 ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm"); 324 err = set_delegate_mask(fs_fd, "delegate_maps", 0x1, NULL); 325 ASSERT_EQ(err, -EPERM, "delegate_maps_eperm"); 326 err = set_delegate_mask(fs_fd, "delegate_progs", 0x1, NULL); 327 ASSERT_EQ(err, -EPERM, "delegate_progs_eperm"); 328 err = set_delegate_mask(fs_fd, "delegate_attachs", 0x1, NULL); 329 ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm"); 330 331 /* pass BPF FS context object to parent */ 332 err = sendfd(sock_fd, fs_fd); 333 if (!ASSERT_OK(err, "send_fs_fd")) 334 goto cleanup; 335 zclose(fs_fd); 336 337 /* avoid mucking around with mount namespaces and mounting at 338 * well-known path, just get detach-mounted BPF FS fd back from parent 339 */ 340 err = recvfd(sock_fd, &mnt_fd); 341 if (!ASSERT_OK(err, "recv_mnt_fd")) 342 goto cleanup; 343 344 /* try to fspick() BPF FS and try to add some delegation options */ 345 fs_fd = sys_fspick(mnt_fd, "", FSPICK_EMPTY_PATH); 346 if (!ASSERT_GE(fs_fd, 0, "bpffs_fspick")) { 347 err = -EINVAL; 348 goto cleanup; 349 } 350 351 /* ensure unprivileged child cannot reconfigure to set delegation options */ 352 err = set_delegate_mask(fs_fd, "delegate_cmds", 0, "any"); 353 if (!ASSERT_EQ(err, -EPERM, "delegate_cmd_eperm_reconfig")) { 354 err = -EINVAL; 355 goto cleanup; 356 } 357 err = set_delegate_mask(fs_fd, "delegate_maps", 0, "any"); 358 if (!ASSERT_EQ(err, -EPERM, "delegate_maps_eperm_reconfig")) { 359 err = -EINVAL; 360 goto cleanup; 361 } 362 err = set_delegate_mask(fs_fd, "delegate_progs", 0, "any"); 363 if (!ASSERT_EQ(err, -EPERM, "delegate_progs_eperm_reconfig")) { 364 err = -EINVAL; 365 goto cleanup; 366 } 367 err = set_delegate_mask(fs_fd, "delegate_attachs", 0, "any"); 368 if (!ASSERT_EQ(err, -EPERM, "delegate_attachs_eperm_reconfig")) { 369 err = -EINVAL; 370 goto cleanup; 371 } 372 zclose(fs_fd); 373 374 bpffs_fd = openat(mnt_fd, ".", 0, O_RDWR); 375 if (!ASSERT_GE(bpffs_fd, 0, "bpffs_open")) { 376 err = -EINVAL; 377 goto cleanup; 378 } 379 380 /* create BPF token FD and pass it to parent for some extra checks */ 381 token_fd = bpf_token_create(bpffs_fd, NULL); 382 if (!ASSERT_GT(token_fd, 0, "child_token_create")) { 383 err = -EINVAL; 384 goto cleanup; 385 } 386 err = sendfd(sock_fd, token_fd); 387 if (!ASSERT_OK(err, "send_token_fd")) 388 goto cleanup; 389 zclose(token_fd); 390 391 /* do custom test logic with customly set up BPF FS instance */ 392 err = callback(bpffs_fd, lsm_skel); 393 if (!ASSERT_OK(err, "test_callback")) 394 goto cleanup; 395 396 err = 0; 397 cleanup: 398 zclose(sock_fd); 399 zclose(mnt_fd); 400 zclose(fs_fd); 401 zclose(bpffs_fd); 402 zclose(token_fd); 403 404 lsm_skel->bss->my_pid = 0; 405 token_lsm__destroy(lsm_skel); 406 407 exit(-err); 408 } 409 410 static int wait_for_pid(pid_t pid) 411 { 412 int status, ret; 413 414 again: 415 ret = waitpid(pid, &status, 0); 416 if (ret == -1) { 417 if (errno == EINTR) 418 goto again; 419 420 return -1; 421 } 422 423 if (!WIFEXITED(status)) 424 return -1; 425 426 return WEXITSTATUS(status); 427 } 428 429 static void parent(int child_pid, struct bpffs_opts *bpffs_opts, int sock_fd) 430 { 431 int fs_fd = -1, mnt_fd = -1, token_fd = -1, err; 432 433 err = recvfd(sock_fd, &fs_fd); 434 if (!ASSERT_OK(err, "recv_bpffs_fd")) 435 goto cleanup; 436 437 mnt_fd = materialize_bpffs_fd(fs_fd, bpffs_opts); 438 if (!ASSERT_GE(mnt_fd, 0, "materialize_bpffs_fd")) { 439 err = -EINVAL; 440 goto cleanup; 441 } 442 zclose(fs_fd); 443 444 /* pass BPF FS context object to parent */ 445 err = sendfd(sock_fd, mnt_fd); 446 if (!ASSERT_OK(err, "send_mnt_fd")) 447 goto cleanup; 448 zclose(mnt_fd); 449 450 /* receive BPF token FD back from child for some extra tests */ 451 err = recvfd(sock_fd, &token_fd); 452 if (!ASSERT_OK(err, "recv_token_fd")) 453 goto cleanup; 454 455 err = wait_for_pid(child_pid); 456 ASSERT_OK(err, "waitpid_child"); 457 458 cleanup: 459 zclose(sock_fd); 460 zclose(fs_fd); 461 zclose(mnt_fd); 462 zclose(token_fd); 463 464 if (child_pid > 0) 465 (void)kill(child_pid, SIGKILL); 466 } 467 468 static void subtest_userns(struct bpffs_opts *bpffs_opts, 469 child_callback_fn child_cb) 470 { 471 int sock_fds[2] = { -1, -1 }; 472 int child_pid = 0, err; 473 474 err = socketpair(AF_UNIX, SOCK_STREAM, 0, sock_fds); 475 if (!ASSERT_OK(err, "socketpair")) 476 goto cleanup; 477 478 child_pid = fork(); 479 if (!ASSERT_GE(child_pid, 0, "fork")) 480 goto cleanup; 481 482 if (child_pid == 0) { 483 zclose(sock_fds[0]); 484 return child(sock_fds[1], bpffs_opts, child_cb); 485 486 } else { 487 zclose(sock_fds[1]); 488 return parent(child_pid, bpffs_opts, sock_fds[0]); 489 } 490 491 cleanup: 492 zclose(sock_fds[0]); 493 zclose(sock_fds[1]); 494 if (child_pid > 0) 495 (void)kill(child_pid, SIGKILL); 496 } 497 498 static int userns_map_create(int mnt_fd, struct token_lsm *lsm_skel) 499 { 500 LIBBPF_OPTS(bpf_map_create_opts, map_opts); 501 int err, token_fd = -1, map_fd = -1; 502 __u64 old_caps = 0; 503 504 /* create BPF token from BPF FS mount */ 505 token_fd = bpf_token_create(mnt_fd, NULL); 506 if (!ASSERT_GT(token_fd, 0, "token_create")) { 507 err = -EINVAL; 508 goto cleanup; 509 } 510 511 /* while inside non-init userns, we need both a BPF token *and* 512 * CAP_BPF inside current userns to create privileged map; let's test 513 * that neither BPF token alone nor namespaced CAP_BPF is sufficient 514 */ 515 err = drop_priv_caps(&old_caps); 516 if (!ASSERT_OK(err, "drop_caps")) 517 goto cleanup; 518 519 /* no token, no CAP_BPF -> fail */ 520 map_opts.map_flags = 0; 521 map_opts.token_fd = 0; 522 map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "wo_token_wo_bpf", 0, 8, 1, &map_opts); 523 if (!ASSERT_LT(map_fd, 0, "stack_map_wo_token_wo_cap_bpf_should_fail")) { 524 err = -EINVAL; 525 goto cleanup; 526 } 527 528 /* token without CAP_BPF -> fail */ 529 map_opts.map_flags = BPF_F_TOKEN_FD; 530 map_opts.token_fd = token_fd; 531 map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "w_token_wo_bpf", 0, 8, 1, &map_opts); 532 if (!ASSERT_LT(map_fd, 0, "stack_map_w_token_wo_cap_bpf_should_fail")) { 533 err = -EINVAL; 534 goto cleanup; 535 } 536 537 /* get back effective local CAP_BPF (and CAP_SYS_ADMIN) */ 538 err = restore_priv_caps(old_caps); 539 if (!ASSERT_OK(err, "restore_caps")) 540 goto cleanup; 541 542 /* CAP_BPF without token -> fail */ 543 map_opts.map_flags = 0; 544 map_opts.token_fd = 0; 545 map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "wo_token_w_bpf", 0, 8, 1, &map_opts); 546 if (!ASSERT_LT(map_fd, 0, "stack_map_wo_token_w_cap_bpf_should_fail")) { 547 err = -EINVAL; 548 goto cleanup; 549 } 550 551 /* finally, namespaced CAP_BPF + token -> success */ 552 map_opts.map_flags = BPF_F_TOKEN_FD; 553 map_opts.token_fd = token_fd; 554 map_fd = bpf_map_create(BPF_MAP_TYPE_STACK, "w_token_w_bpf", 0, 8, 1, &map_opts); 555 if (!ASSERT_GT(map_fd, 0, "stack_map_w_token_w_cap_bpf")) { 556 err = -EINVAL; 557 goto cleanup; 558 } 559 560 cleanup: 561 zclose(token_fd); 562 zclose(map_fd); 563 return err; 564 } 565 566 static int userns_btf_load(int mnt_fd, struct token_lsm *lsm_skel) 567 { 568 LIBBPF_OPTS(bpf_btf_load_opts, btf_opts); 569 int err, token_fd = -1, btf_fd = -1; 570 const void *raw_btf_data; 571 struct btf *btf = NULL; 572 __u32 raw_btf_size; 573 __u64 old_caps = 0; 574 575 /* create BPF token from BPF FS mount */ 576 token_fd = bpf_token_create(mnt_fd, NULL); 577 if (!ASSERT_GT(token_fd, 0, "token_create")) { 578 err = -EINVAL; 579 goto cleanup; 580 } 581 582 /* while inside non-init userns, we need both a BPF token *and* 583 * CAP_BPF inside current userns to create privileged map; let's test 584 * that neither BPF token alone nor namespaced CAP_BPF is sufficient 585 */ 586 err = drop_priv_caps(&old_caps); 587 if (!ASSERT_OK(err, "drop_caps")) 588 goto cleanup; 589 590 /* setup a trivial BTF data to load to the kernel */ 591 btf = btf__new_empty(); 592 if (!ASSERT_OK_PTR(btf, "empty_btf")) 593 goto cleanup; 594 595 ASSERT_GT(btf__add_int(btf, "int", 4, 0), 0, "int_type"); 596 597 raw_btf_data = btf__raw_data(btf, &raw_btf_size); 598 if (!ASSERT_OK_PTR(raw_btf_data, "raw_btf_data")) 599 goto cleanup; 600 601 /* no token + no CAP_BPF -> failure */ 602 btf_opts.btf_flags = 0; 603 btf_opts.token_fd = 0; 604 btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts); 605 if (!ASSERT_LT(btf_fd, 0, "no_token_no_cap_should_fail")) 606 goto cleanup; 607 608 /* token + no CAP_BPF -> failure */ 609 btf_opts.btf_flags = BPF_F_TOKEN_FD; 610 btf_opts.token_fd = token_fd; 611 btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts); 612 if (!ASSERT_LT(btf_fd, 0, "token_no_cap_should_fail")) 613 goto cleanup; 614 615 /* get back effective local CAP_BPF (and CAP_SYS_ADMIN) */ 616 err = restore_priv_caps(old_caps); 617 if (!ASSERT_OK(err, "restore_caps")) 618 goto cleanup; 619 620 /* token + CAP_BPF -> success */ 621 btf_opts.btf_flags = BPF_F_TOKEN_FD; 622 btf_opts.token_fd = token_fd; 623 btf_fd = bpf_btf_load(raw_btf_data, raw_btf_size, &btf_opts); 624 if (!ASSERT_GT(btf_fd, 0, "token_and_cap_success")) 625 goto cleanup; 626 627 err = 0; 628 cleanup: 629 btf__free(btf); 630 zclose(btf_fd); 631 zclose(token_fd); 632 return err; 633 } 634 635 static int userns_prog_load(int mnt_fd, struct token_lsm *lsm_skel) 636 { 637 LIBBPF_OPTS(bpf_prog_load_opts, prog_opts); 638 int err, token_fd = -1, prog_fd = -1; 639 struct bpf_insn insns[] = { 640 /* bpf_jiffies64() requires CAP_BPF */ 641 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_jiffies64), 642 /* bpf_get_current_task() requires CAP_PERFMON */ 643 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_current_task), 644 /* r0 = 0; exit; */ 645 BPF_MOV64_IMM(BPF_REG_0, 0), 646 BPF_EXIT_INSN(), 647 }; 648 size_t insn_cnt = ARRAY_SIZE(insns); 649 __u64 old_caps = 0; 650 651 /* create BPF token from BPF FS mount */ 652 token_fd = bpf_token_create(mnt_fd, NULL); 653 if (!ASSERT_GT(token_fd, 0, "token_create")) { 654 err = -EINVAL; 655 goto cleanup; 656 } 657 658 /* validate we can successfully load BPF program with token; this 659 * being XDP program (CAP_NET_ADMIN) using bpf_jiffies64() (CAP_BPF) 660 * and bpf_get_current_task() (CAP_PERFMON) helpers validates we have 661 * BPF token wired properly in a bunch of places in the kernel 662 */ 663 prog_opts.prog_flags = BPF_F_TOKEN_FD; 664 prog_opts.token_fd = token_fd; 665 prog_opts.expected_attach_type = BPF_XDP; 666 prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL", 667 insns, insn_cnt, &prog_opts); 668 if (!ASSERT_GT(prog_fd, 0, "prog_fd")) { 669 err = -EPERM; 670 goto cleanup; 671 } 672 673 /* no token + caps -> failure */ 674 prog_opts.prog_flags = 0; 675 prog_opts.token_fd = 0; 676 prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL", 677 insns, insn_cnt, &prog_opts); 678 if (!ASSERT_EQ(prog_fd, -EPERM, "prog_fd_eperm")) { 679 err = -EPERM; 680 goto cleanup; 681 } 682 683 err = drop_priv_caps(&old_caps); 684 if (!ASSERT_OK(err, "drop_caps")) 685 goto cleanup; 686 687 /* no caps + token -> failure */ 688 prog_opts.prog_flags = BPF_F_TOKEN_FD; 689 prog_opts.token_fd = token_fd; 690 prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL", 691 insns, insn_cnt, &prog_opts); 692 if (!ASSERT_EQ(prog_fd, -EPERM, "prog_fd_eperm")) { 693 err = -EPERM; 694 goto cleanup; 695 } 696 697 /* no caps + no token -> definitely a failure */ 698 prog_opts.prog_flags = 0; 699 prog_opts.token_fd = 0; 700 prog_fd = bpf_prog_load(BPF_PROG_TYPE_XDP, "token_prog", "GPL", 701 insns, insn_cnt, &prog_opts); 702 if (!ASSERT_EQ(prog_fd, -EPERM, "prog_fd_eperm")) { 703 err = -EPERM; 704 goto cleanup; 705 } 706 707 err = 0; 708 cleanup: 709 zclose(prog_fd); 710 zclose(token_fd); 711 return err; 712 } 713 714 static int userns_obj_priv_map(int mnt_fd, struct token_lsm *lsm_skel) 715 { 716 LIBBPF_OPTS(bpf_object_open_opts, opts); 717 char buf[256]; 718 struct priv_map *skel; 719 int err; 720 721 skel = priv_map__open_and_load(); 722 if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) { 723 priv_map__destroy(skel); 724 return -EINVAL; 725 } 726 727 /* use bpf_token_path to provide BPF FS path */ 728 snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd); 729 opts.bpf_token_path = buf; 730 skel = priv_map__open_opts(&opts); 731 if (!ASSERT_OK_PTR(skel, "obj_token_path_open")) 732 return -EINVAL; 733 734 err = priv_map__load(skel); 735 priv_map__destroy(skel); 736 if (!ASSERT_OK(err, "obj_token_path_load")) 737 return -EINVAL; 738 739 return 0; 740 } 741 742 static int userns_obj_priv_prog(int mnt_fd, struct token_lsm *lsm_skel) 743 { 744 LIBBPF_OPTS(bpf_object_open_opts, opts); 745 char buf[256]; 746 struct priv_prog *skel; 747 int err; 748 749 skel = priv_prog__open_and_load(); 750 if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) { 751 priv_prog__destroy(skel); 752 return -EINVAL; 753 } 754 755 /* use bpf_token_path to provide BPF FS path */ 756 snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd); 757 opts.bpf_token_path = buf; 758 skel = priv_prog__open_opts(&opts); 759 if (!ASSERT_OK_PTR(skel, "obj_token_path_open")) 760 return -EINVAL; 761 err = priv_prog__load(skel); 762 priv_prog__destroy(skel); 763 if (!ASSERT_OK(err, "obj_token_path_load")) 764 return -EINVAL; 765 766 /* provide BPF token, but reject bpf_token_capable() with LSM */ 767 lsm_skel->bss->reject_capable = true; 768 lsm_skel->bss->reject_cmd = false; 769 skel = priv_prog__open_opts(&opts); 770 if (!ASSERT_OK_PTR(skel, "obj_token_lsm_reject_cap_open")) 771 return -EINVAL; 772 err = priv_prog__load(skel); 773 priv_prog__destroy(skel); 774 if (!ASSERT_ERR(err, "obj_token_lsm_reject_cap_load")) 775 return -EINVAL; 776 777 /* provide BPF token, but reject bpf_token_cmd() with LSM */ 778 lsm_skel->bss->reject_capable = false; 779 lsm_skel->bss->reject_cmd = true; 780 skel = priv_prog__open_opts(&opts); 781 if (!ASSERT_OK_PTR(skel, "obj_token_lsm_reject_cmd_open")) 782 return -EINVAL; 783 err = priv_prog__load(skel); 784 priv_prog__destroy(skel); 785 if (!ASSERT_ERR(err, "obj_token_lsm_reject_cmd_load")) 786 return -EINVAL; 787 788 return 0; 789 } 790 791 /* this test is called with BPF FS that doesn't delegate BPF_BTF_LOAD command, 792 * which should cause struct_ops application to fail, as BTF won't be uploaded 793 * into the kernel, even if STRUCT_OPS programs themselves are allowed 794 */ 795 static int validate_struct_ops_load(int mnt_fd, bool expect_success) 796 { 797 LIBBPF_OPTS(bpf_object_open_opts, opts); 798 char buf[256]; 799 struct dummy_st_ops_success *skel; 800 int err; 801 802 snprintf(buf, sizeof(buf), "/proc/self/fd/%d", mnt_fd); 803 opts.bpf_token_path = buf; 804 skel = dummy_st_ops_success__open_opts(&opts); 805 if (!ASSERT_OK_PTR(skel, "obj_token_path_open")) 806 return -EINVAL; 807 808 err = dummy_st_ops_success__load(skel); 809 dummy_st_ops_success__destroy(skel); 810 if (expect_success) { 811 if (!ASSERT_OK(err, "obj_token_path_load")) 812 return -EINVAL; 813 } else /* expect failure */ { 814 if (!ASSERT_ERR(err, "obj_token_path_load")) 815 return -EINVAL; 816 } 817 818 return 0; 819 } 820 821 static int userns_obj_priv_btf_fail(int mnt_fd, struct token_lsm *lsm_skel) 822 { 823 return validate_struct_ops_load(mnt_fd, false /* should fail */); 824 } 825 826 static int userns_obj_priv_btf_success(int mnt_fd, struct token_lsm *lsm_skel) 827 { 828 return validate_struct_ops_load(mnt_fd, true /* should succeed */); 829 } 830 831 #define TOKEN_ENVVAR "LIBBPF_BPF_TOKEN_PATH" 832 #define TOKEN_BPFFS_CUSTOM "/bpf-token-fs" 833 834 static int userns_obj_priv_implicit_token(int mnt_fd, struct token_lsm *lsm_skel) 835 { 836 LIBBPF_OPTS(bpf_object_open_opts, opts); 837 struct dummy_st_ops_success *skel; 838 int err; 839 840 /* before we mount BPF FS with token delegation, struct_ops skeleton 841 * should fail to load 842 */ 843 skel = dummy_st_ops_success__open_and_load(); 844 if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) { 845 dummy_st_ops_success__destroy(skel); 846 return -EINVAL; 847 } 848 849 /* mount custom BPF FS over /sys/fs/bpf so that libbpf can create BPF 850 * token automatically and implicitly 851 */ 852 err = sys_move_mount(mnt_fd, "", AT_FDCWD, "/sys/fs/bpf", MOVE_MOUNT_F_EMPTY_PATH); 853 if (!ASSERT_OK(err, "move_mount_bpffs")) 854 return -EINVAL; 855 856 /* disable implicit BPF token creation by setting 857 * LIBBPF_BPF_TOKEN_PATH envvar to empty value, load should fail 858 */ 859 err = setenv(TOKEN_ENVVAR, "", 1 /*overwrite*/); 860 if (!ASSERT_OK(err, "setenv_token_path")) 861 return -EINVAL; 862 skel = dummy_st_ops_success__open_and_load(); 863 if (!ASSERT_ERR_PTR(skel, "obj_token_envvar_disabled_load")) { 864 unsetenv(TOKEN_ENVVAR); 865 dummy_st_ops_success__destroy(skel); 866 return -EINVAL; 867 } 868 unsetenv(TOKEN_ENVVAR); 869 870 /* now the same struct_ops skeleton should succeed thanks to libppf 871 * creating BPF token from /sys/fs/bpf mount point 872 */ 873 skel = dummy_st_ops_success__open_and_load(); 874 if (!ASSERT_OK_PTR(skel, "obj_implicit_token_load")) 875 return -EINVAL; 876 877 dummy_st_ops_success__destroy(skel); 878 879 /* now disable implicit token through empty bpf_token_path, should fail */ 880 opts.bpf_token_path = ""; 881 skel = dummy_st_ops_success__open_opts(&opts); 882 if (!ASSERT_OK_PTR(skel, "obj_empty_token_path_open")) 883 return -EINVAL; 884 885 err = dummy_st_ops_success__load(skel); 886 dummy_st_ops_success__destroy(skel); 887 if (!ASSERT_ERR(err, "obj_empty_token_path_load")) 888 return -EINVAL; 889 890 return 0; 891 } 892 893 static int userns_obj_priv_implicit_token_envvar(int mnt_fd, struct token_lsm *lsm_skel) 894 { 895 LIBBPF_OPTS(bpf_object_open_opts, opts); 896 struct dummy_st_ops_success *skel; 897 int err; 898 899 /* before we mount BPF FS with token delegation, struct_ops skeleton 900 * should fail to load 901 */ 902 skel = dummy_st_ops_success__open_and_load(); 903 if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load")) { 904 dummy_st_ops_success__destroy(skel); 905 return -EINVAL; 906 } 907 908 /* mount custom BPF FS over custom location, so libbpf can't create 909 * BPF token implicitly, unless pointed to it through 910 * LIBBPF_BPF_TOKEN_PATH envvar 911 */ 912 rmdir(TOKEN_BPFFS_CUSTOM); 913 if (!ASSERT_OK(mkdir(TOKEN_BPFFS_CUSTOM, 0777), "mkdir_bpffs_custom")) 914 goto err_out; 915 err = sys_move_mount(mnt_fd, "", AT_FDCWD, TOKEN_BPFFS_CUSTOM, MOVE_MOUNT_F_EMPTY_PATH); 916 if (!ASSERT_OK(err, "move_mount_bpffs")) 917 goto err_out; 918 919 /* even though we have BPF FS with delegation, it's not at default 920 * /sys/fs/bpf location, so we still fail to load until envvar is set up 921 */ 922 skel = dummy_st_ops_success__open_and_load(); 923 if (!ASSERT_ERR_PTR(skel, "obj_tokenless_load2")) { 924 dummy_st_ops_success__destroy(skel); 925 goto err_out; 926 } 927 928 err = setenv(TOKEN_ENVVAR, TOKEN_BPFFS_CUSTOM, 1 /*overwrite*/); 929 if (!ASSERT_OK(err, "setenv_token_path")) 930 goto err_out; 931 932 /* now the same struct_ops skeleton should succeed thanks to libppf 933 * creating BPF token from custom mount point 934 */ 935 skel = dummy_st_ops_success__open_and_load(); 936 if (!ASSERT_OK_PTR(skel, "obj_implicit_token_load")) 937 goto err_out; 938 939 dummy_st_ops_success__destroy(skel); 940 941 /* now disable implicit token through empty bpf_token_path, envvar 942 * will be ignored, should fail 943 */ 944 opts.bpf_token_path = ""; 945 skel = dummy_st_ops_success__open_opts(&opts); 946 if (!ASSERT_OK_PTR(skel, "obj_empty_token_path_open")) 947 goto err_out; 948 949 err = dummy_st_ops_success__load(skel); 950 dummy_st_ops_success__destroy(skel); 951 if (!ASSERT_ERR(err, "obj_empty_token_path_load")) 952 goto err_out; 953 954 rmdir(TOKEN_BPFFS_CUSTOM); 955 unsetenv(TOKEN_ENVVAR); 956 return 0; 957 err_out: 958 rmdir(TOKEN_BPFFS_CUSTOM); 959 unsetenv(TOKEN_ENVVAR); 960 return -EINVAL; 961 } 962 963 #define bit(n) (1ULL << (n)) 964 965 void test_token(void) 966 { 967 if (test__start_subtest("map_token")) { 968 struct bpffs_opts opts = { 969 .cmds_str = "map_create", 970 .maps_str = "stack", 971 }; 972 973 subtest_userns(&opts, userns_map_create); 974 } 975 if (test__start_subtest("btf_token")) { 976 struct bpffs_opts opts = { 977 .cmds = 1ULL << BPF_BTF_LOAD, 978 }; 979 980 subtest_userns(&opts, userns_btf_load); 981 } 982 if (test__start_subtest("prog_token")) { 983 struct bpffs_opts opts = { 984 .cmds_str = "PROG_LOAD", 985 .progs_str = "XDP", 986 .attachs_str = "xdp", 987 }; 988 989 subtest_userns(&opts, userns_prog_load); 990 } 991 if (test__start_subtest("obj_priv_map")) { 992 struct bpffs_opts opts = { 993 .cmds = bit(BPF_MAP_CREATE), 994 .maps = bit(BPF_MAP_TYPE_QUEUE), 995 }; 996 997 subtest_userns(&opts, userns_obj_priv_map); 998 } 999 if (test__start_subtest("obj_priv_prog")) { 1000 struct bpffs_opts opts = { 1001 .cmds = bit(BPF_PROG_LOAD), 1002 .progs = bit(BPF_PROG_TYPE_KPROBE), 1003 .attachs = ~0ULL, 1004 }; 1005 1006 subtest_userns(&opts, userns_obj_priv_prog); 1007 } 1008 if (test__start_subtest("obj_priv_btf_fail")) { 1009 struct bpffs_opts opts = { 1010 /* disallow BTF loading */ 1011 .cmds = bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD), 1012 .maps = bit(BPF_MAP_TYPE_STRUCT_OPS), 1013 .progs = bit(BPF_PROG_TYPE_STRUCT_OPS), 1014 .attachs = ~0ULL, 1015 }; 1016 1017 subtest_userns(&opts, userns_obj_priv_btf_fail); 1018 } 1019 if (test__start_subtest("obj_priv_btf_success")) { 1020 struct bpffs_opts opts = { 1021 /* allow BTF loading */ 1022 .cmds = bit(BPF_BTF_LOAD) | bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD), 1023 .maps = bit(BPF_MAP_TYPE_STRUCT_OPS), 1024 .progs = bit(BPF_PROG_TYPE_STRUCT_OPS), 1025 .attachs = ~0ULL, 1026 }; 1027 1028 subtest_userns(&opts, userns_obj_priv_btf_success); 1029 } 1030 if (test__start_subtest("obj_priv_implicit_token")) { 1031 struct bpffs_opts opts = { 1032 /* allow BTF loading */ 1033 .cmds = bit(BPF_BTF_LOAD) | bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD), 1034 .maps = bit(BPF_MAP_TYPE_STRUCT_OPS), 1035 .progs = bit(BPF_PROG_TYPE_STRUCT_OPS), 1036 .attachs = ~0ULL, 1037 }; 1038 1039 subtest_userns(&opts, userns_obj_priv_implicit_token); 1040 } 1041 if (test__start_subtest("obj_priv_implicit_token_envvar")) { 1042 struct bpffs_opts opts = { 1043 /* allow BTF loading */ 1044 .cmds = bit(BPF_BTF_LOAD) | bit(BPF_MAP_CREATE) | bit(BPF_PROG_LOAD), 1045 .maps = bit(BPF_MAP_TYPE_STRUCT_OPS), 1046 .progs = bit(BPF_PROG_TYPE_STRUCT_OPS), 1047 .attachs = ~0ULL, 1048 }; 1049 1050 subtest_userns(&opts, userns_obj_priv_implicit_token_envvar); 1051 } 1052 } 1053
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.