| 1 | |
| 2 | |
| 3 | * KUnit tests for commoncap.c security functions
|
| 4 | *
|
| 5 | * Tests for security-critical functions in the capability subsystem,
|
| 6 | * particularly namespace-related capability checks.
|
| 7 |
|
| 8 |
|
| 9 | #include <kunit/test.h>
|
| 10 | #include <linux/user_namespace.h>
|
| 11 | #include <linux/uidgid.h>
|
| 12 | #include <linux/cred.h>
|
| 13 | #include <linux/mnt_idmapping.h>
|
| 14 | #include <linux/module.h>
|
| 15 | #include <linux/slab.h>
|
| 16 | #include <linux/refcount.h>
|
| 17 |
|
| 18 | #ifdef CONFIG_SECURITY_COMMONCAP_KUNIT_TEST
|
| 19 |
|
| 20 | |
| 21 | * included in the same compilation unit when tests are enabled.
|
| 22 |
|
| 23 |
|
| 24 | |
| 25 | * test_vfsuid_root_in_currentns_init_ns - Test vfsuid_root_in_currentns with init ns
|
| 26 | *
|
| 27 | * Verifies that UID 0 in the init namespace correctly owns the current
|
| 28 | * namespace when running in init_user_ns.
|
| 29 | *
|
| 30 | * @test: KUnit test context
|
| 31 |
|
| 32 | static void test_vfsuid_root_in_currentns_init_ns(struct kunit *test)
|
| 33 | {
|
| 34 | vfsuid_t vfsuid;
|
| 35 | kuid_t kuid;
|
| 36 |
|
| 37 |
|
| 38 | kuid = KUIDT_INIT(0);
|
| 39 | vfsuid = VFSUIDT_INIT(kuid);
|
| 40 |
|
| 41 |
|
| 42 | KUNIT_EXPECT_TRUE(test, vfsuid_root_in_currentns(vfsuid));
|
| 43 | }
|
| 44 |
|
| 45 | |
| 46 | * test_vfsuid_root_in_currentns_invalid - Test vfsuid_root_in_currentns with invalid vfsuid
|
| 47 | *
|
| 48 | * Verifies that an invalid vfsuid correctly returns false.
|
| 49 | *
|
| 50 | * @test: KUnit test context
|
| 51 |
|
| 52 | static void test_vfsuid_root_in_currentns_invalid(struct kunit *test)
|
| 53 | {
|
| 54 | vfsuid_t invalid_vfsuid;
|
| 55 |
|
| 56 |
|
| 57 | invalid_vfsuid = INVALID_VFSUID;
|
| 58 |
|
| 59 |
|
| 60 | KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(invalid_vfsuid));
|
| 61 | }
|
| 62 |
|
| 63 | |
| 64 | * test_vfsuid_root_in_currentns_nonzero - Test vfsuid_root_in_currentns with non-zero UID
|
| 65 | *
|
| 66 | * Verifies that a non-zero UID correctly returns false.
|
| 67 | *
|
| 68 | * @test: KUnit test context
|
| 69 |
|
| 70 | static void test_vfsuid_root_in_currentns_nonzero(struct kunit *test)
|
| 71 | {
|
| 72 | vfsuid_t vfsuid;
|
| 73 | kuid_t kuid;
|
| 74 |
|
| 75 |
|
| 76 | kuid = KUIDT_INIT(1000);
|
| 77 | vfsuid = VFSUIDT_INIT(kuid);
|
| 78 |
|
| 79 |
|
| 80 | KUNIT_EXPECT_FALSE(test, vfsuid_root_in_currentns(vfsuid));
|
| 81 | }
|
| 82 |
|
| 83 | |
| 84 | * test_kuid_root_in_ns_init_ns_uid0 - Test kuid_root_in_ns with init namespace and UID 0
|
| 85 | *
|
| 86 | * Verifies that kuid_root_in_ns correctly identifies UID 0 in init namespace.
|
| 87 | * This tests the core namespace traversal logic. In init namespace, UID 0
|
| 88 | * maps to itself, so it should own the namespace.
|
| 89 | *
|
| 90 | * @test: KUnit test context
|
| 91 |
|
| 92 | static void test_kuid_root_in_ns_init_ns_uid0(struct kunit *test)
|
| 93 | {
|
| 94 | kuid_t kuid;
|
| 95 | struct user_namespace *init_ns;
|
| 96 |
|
| 97 | kuid = KUIDT_INIT(0);
|
| 98 | init_ns = &init_user_ns;
|
| 99 |
|
| 100 |
|
| 101 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(kuid, init_ns));
|
| 102 | }
|
| 103 |
|
| 104 | |
| 105 | * test_kuid_root_in_ns_init_ns_nonzero - Test kuid_root_in_ns with init namespace and non-zero UID
|
| 106 | *
|
| 107 | * Verifies that kuid_root_in_ns correctly rejects non-zero UIDs in init namespace.
|
| 108 | * Only UID 0 should own a namespace.
|
| 109 | *
|
| 110 | * @test: KUnit test context
|
| 111 |
|
| 112 | static void test_kuid_root_in_ns_init_ns_nonzero(struct kunit *test)
|
| 113 | {
|
| 114 | kuid_t kuid;
|
| 115 | struct user_namespace *init_ns;
|
| 116 |
|
| 117 | kuid = KUIDT_INIT(1000);
|
| 118 | init_ns = &init_user_ns;
|
| 119 |
|
| 120 |
|
| 121 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(kuid, init_ns));
|
| 122 | }
|
| 123 |
|
| 124 | |
| 125 | * create_test_user_ns_with_mapping - Create a mock user namespace with UID mapping
|
| 126 | *
|
| 127 | * Creates a minimal user namespace structure for testing where uid 0 in the
|
| 128 | * namespace maps to a specific kuid in the parent namespace.
|
| 129 | *
|
| 130 | * @test: KUnit test context
|
| 131 | * @parent_ns: Parent namespace (typically init_user_ns)
|
| 132 | * @mapped_kuid: The kuid that uid 0 in this namespace maps to in parent
|
| 133 | *
|
| 134 | * Returns: Pointer to allocated namespace, or NULL on failure
|
| 135 |
|
| 136 | static struct user_namespace *create_test_user_ns_with_mapping(struct kunit *test,
|
| 137 | struct user_namespace *parent_ns,
|
| 138 | kuid_t mapped_kuid)
|
| 139 | {
|
| 140 | struct user_namespace *ns;
|
| 141 | struct uid_gid_extent extent;
|
| 142 |
|
| 143 |
|
| 144 | ns = kunit_kzalloc(test, sizeof(*ns), GFP_KERNEL);
|
| 145 | if (!ns)
|
| 146 | return NULL;
|
| 147 |
|
| 148 |
|
| 149 | ns->parent = parent_ns;
|
| 150 | ns->level = parent_ns ? parent_ns->level + 1 : 0;
|
| 151 | ns->owner = mapped_kuid;
|
| 152 | ns->group = KGIDT_INIT(0);
|
| 153 |
|
| 154 |
|
| 155 | refcount_set(&ns->ns.__ns_ref, 1);
|
| 156 | ns->ns.inum = 0;
|
| 157 |
|
| 158 | |
| 159 | * Format: first (uid in ns) : lower_first (kuid in parent) : count
|
| 160 | * So: uid 0 in ns -> kuid mapped_kuid in parent
|
| 161 | * This means from_kuid(ns, mapped_kuid) returns 0
|
| 162 |
|
| 163 | extent.first = 0;
|
| 164 | extent.lower_first = __kuid_val(mapped_kuid);
|
| 165 | extent.count = 1;
|
| 166 |
|
| 167 | ns->uid_map.extent[0] = extent;
|
| 168 | ns->uid_map.nr_extents = 1;
|
| 169 |
|
| 170 |
|
| 171 | extent.first = 0;
|
| 172 | extent.lower_first = 0;
|
| 173 | extent.count = 1;
|
| 174 |
|
| 175 | ns->gid_map.extent[0] = extent;
|
| 176 | ns->gid_map.nr_extents = 1;
|
| 177 |
|
| 178 | return ns;
|
| 179 | }
|
| 180 |
|
| 181 | |
| 182 | * test_kuid_root_in_ns_with_mapping - Test kuid_root_in_ns with namespace where uid 0
|
| 183 | * maps to different kuid
|
| 184 | *
|
| 185 | * Creates a user namespace where uid 0 maps to kuid 1000 in the parent namespace.
|
| 186 | * Verifies that kuid_root_in_ns correctly identifies kuid 1000 as owning the namespace.
|
| 187 | *
|
| 188 | * Note: kuid_root_in_ns walks up the namespace hierarchy, so it checks the current
|
| 189 | * namespace first, then parent, then parent's parent, etc. So:
|
| 190 | * - kuid 1000 owns test_ns because from_kuid(test_ns, 1000) == 0
|
| 191 | * - kuid 0 also owns test_ns because from_kuid(init_user_ns, 0) == 0
|
| 192 | * (checked in parent)
|
| 193 | *
|
| 194 | * This tests the actual functionality as requested: creating namespaces with
|
| 195 | * different values for the namespace's uid 0.
|
| 196 | *
|
| 197 | * @test: KUnit test context
|
| 198 |
|
| 199 | static void test_kuid_root_in_ns_with_mapping(struct kunit *test)
|
| 200 | {
|
| 201 | struct user_namespace *test_ns;
|
| 202 | struct user_namespace *parent_ns;
|
| 203 | kuid_t mapped_kuid, other_kuid;
|
| 204 |
|
| 205 | parent_ns = &init_user_ns;
|
| 206 | mapped_kuid = KUIDT_INIT(1000);
|
| 207 | other_kuid = KUIDT_INIT(2000);
|
| 208 |
|
| 209 | test_ns = create_test_user_ns_with_mapping(test, parent_ns, mapped_kuid);
|
| 210 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, test_ns);
|
| 211 |
|
| 212 |
|
| 213 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(mapped_kuid, test_ns));
|
| 214 |
|
| 215 |
|
| 216 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), test_ns));
|
| 217 |
|
| 218 |
|
| 219 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(other_kuid, test_ns));
|
| 220 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(500), test_ns));
|
| 221 | }
|
| 222 |
|
| 223 | |
| 224 | * test_kuid_root_in_ns_with_different_mappings - Test with multiple namespaces
|
| 225 | *
|
| 226 | * Creates multiple user namespaces with different UID mappings to verify
|
| 227 | * that kuid_root_in_ns correctly distinguishes between namespaces.
|
| 228 | *
|
| 229 | * Each namespace maps uid 0 to a different kuid, and we verify that each
|
| 230 | * kuid only owns its corresponding namespace (plus kuid 0 owns all via
|
| 231 | * init_user_ns parent).
|
| 232 | *
|
| 233 | * @test: KUnit test context
|
| 234 |
|
| 235 | static void test_kuid_root_in_ns_with_different_mappings(struct kunit *test)
|
| 236 | {
|
| 237 | struct user_namespace *ns1, *ns2, *ns3;
|
| 238 |
|
| 239 |
|
| 240 | ns1 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(1000));
|
| 241 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns1);
|
| 242 |
|
| 243 | ns2 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(2000));
|
| 244 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns2);
|
| 245 |
|
| 246 | ns3 = create_test_user_ns_with_mapping(test, &init_user_ns, KUIDT_INIT(3000));
|
| 247 | KUNIT_ASSERT_NOT_ERR_OR_NULL(test, ns3);
|
| 248 |
|
| 249 |
|
| 250 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns1));
|
| 251 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns1));
|
| 252 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns1));
|
| 253 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns1));
|
| 254 |
|
| 255 |
|
| 256 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns2));
|
| 257 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns2));
|
| 258 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns2));
|
| 259 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns2));
|
| 260 |
|
| 261 |
|
| 262 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(3000), ns3));
|
| 263 | KUNIT_EXPECT_TRUE(test, kuid_root_in_ns(KUIDT_INIT(0), ns3));
|
| 264 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(1000), ns3));
|
| 265 | KUNIT_EXPECT_FALSE(test, kuid_root_in_ns(KUIDT_INIT(2000), ns3));
|
| 266 | }
|
| 267 |
|
| 268 | static struct kunit_case commoncap_test_cases[] = {
|
| 269 | KUNIT_CASE(test_vfsuid_root_in_currentns_init_ns),
|
| 270 | KUNIT_CASE(test_vfsuid_root_in_currentns_invalid),
|
| 271 | KUNIT_CASE(test_vfsuid_root_in_currentns_nonzero),
|
| 272 | KUNIT_CASE(test_kuid_root_in_ns_init_ns_uid0),
|
| 273 | KUNIT_CASE(test_kuid_root_in_ns_init_ns_nonzero),
|
| 274 | KUNIT_CASE(test_kuid_root_in_ns_with_mapping),
|
| 275 | KUNIT_CASE(test_kuid_root_in_ns_with_different_mappings),
|
| 276 | {}
|
| 277 | };
|
| 278 |
|
| 279 | static struct kunit_suite commoncap_test_suite = {
|
| 280 | .name = "commoncap",
|
| 281 | .test_cases = commoncap_test_cases,
|
| 282 | };
|
| 283 |
|
| 284 | kunit_test_suite(commoncap_test_suite);
|
| 285 |
|
| 286 | MODULE_LICENSE("GPL");
|
| 287 |
|
| 288 | #endif
|