Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/namespaces: seventh listns() permission test

Test that dropping CAP_SYS_ADMIN restricts what we can see.

Link: https://patch.msgid.link/20251029-work-namespace-nstree-listns-v4-54-2e6f823ebdc0@kernel.org
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Christian Brauner <brauner@kernel.org>

+108
+108
tools/testing/selftests/namespaces/listns_permissions_test.c
··· 648 648 TH_LOG("Process can see user namespace it has CAP_SYS_ADMIN inside of"); 649 649 } 650 650 651 + /* 652 + * Test that dropping CAP_SYS_ADMIN restricts what we can see. 653 + */ 654 + TEST(listns_drop_cap_sys_admin) 655 + { 656 + cap_t caps; 657 + cap_value_t cap_list[1] = { CAP_SYS_ADMIN }; 658 + 659 + /* This test needs to start with CAP_SYS_ADMIN */ 660 + caps = cap_get_proc(); 661 + if (!caps) { 662 + SKIP(return, "Cannot get capabilities"); 663 + } 664 + 665 + cap_flag_value_t cap_val; 666 + if (cap_get_flag(caps, CAP_SYS_ADMIN, CAP_EFFECTIVE, &cap_val) < 0) { 667 + cap_free(caps); 668 + SKIP(return, "Cannot check CAP_SYS_ADMIN"); 669 + } 670 + 671 + if (cap_val != CAP_SET) { 672 + cap_free(caps); 673 + SKIP(return, "Test needs CAP_SYS_ADMIN to start"); 674 + } 675 + cap_free(caps); 676 + 677 + int pipefd[2]; 678 + pid_t pid; 679 + int status; 680 + bool correct; 681 + ssize_t count_before, count_after; 682 + 683 + ASSERT_EQ(pipe(pipefd), 0); 684 + 685 + pid = fork(); 686 + ASSERT_GE(pid, 0); 687 + 688 + if (pid == 0) { 689 + struct ns_id_req req = { 690 + .size = sizeof(req), 691 + .spare = 0, 692 + .ns_id = 0, 693 + .ns_type = CLONE_NEWNET, 694 + .spare2 = 0, 695 + .user_ns_id = LISTNS_CURRENT_USER, 696 + }; 697 + __u64 ns_ids_before[100]; 698 + ssize_t count_before; 699 + __u64 ns_ids_after[100]; 700 + ssize_t count_after; 701 + bool correct; 702 + 703 + close(pipefd[0]); 704 + 705 + /* Create user namespace */ 706 + if (setup_userns() < 0) { 707 + close(pipefd[1]); 708 + exit(1); 709 + } 710 + 711 + /* Count namespaces with CAP_SYS_ADMIN */ 712 + count_before = sys_listns(&req, ns_ids_before, ARRAY_SIZE(ns_ids_before), 0); 713 + 714 + /* Drop CAP_SYS_ADMIN */ 715 + caps = cap_get_proc(); 716 + if (caps) { 717 + cap_set_flag(caps, CAP_EFFECTIVE, 1, cap_list, CAP_CLEAR); 718 + cap_set_flag(caps, CAP_PERMITTED, 1, cap_list, CAP_CLEAR); 719 + cap_set_proc(caps); 720 + cap_free(caps); 721 + } 722 + 723 + /* Ensure we can't regain the capability */ 724 + prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0); 725 + 726 + /* Count namespaces without CAP_SYS_ADMIN */ 727 + count_after = sys_listns(&req, ns_ids_after, ARRAY_SIZE(ns_ids_after), 0); 728 + 729 + /* Without CAP_SYS_ADMIN, we should see same or fewer namespaces */ 730 + correct = (count_after <= count_before); 731 + 732 + write(pipefd[1], &correct, sizeof(correct)); 733 + write(pipefd[1], &count_before, sizeof(count_before)); 734 + write(pipefd[1], &count_after, sizeof(count_after)); 735 + close(pipefd[1]); 736 + exit(0); 737 + } 738 + 739 + /* Parent */ 740 + close(pipefd[1]); 741 + 742 + correct = false; 743 + count_before = 0; 744 + count_after = 0; 745 + read(pipefd[0], &correct, sizeof(correct)); 746 + read(pipefd[0], &count_before, sizeof(count_before)); 747 + read(pipefd[0], &count_after, sizeof(count_after)); 748 + close(pipefd[0]); 749 + 750 + waitpid(pid, &status, 0); 751 + ASSERT_TRUE(WIFEXITED(status)); 752 + ASSERT_EQ(WEXITSTATUS(status), 0); 753 + 754 + ASSERT_TRUE(correct); 755 + TH_LOG("With CAP_SYS_ADMIN: %zd namespaces, without: %zd namespaces", 756 + count_before, count_after); 757 + } 758 + 651 759 TEST_HARNESS_MAIN