nsresourced: Add support for delegated ranges
We want to support the scenario where we bind mount the nsresourced varlink socket into a container to allow nested containers where the outer container runs in its own transient range from nsresourced but can still allocate transient ranges for its own nested containers. To support this use case let's add support for delegation. Delegated ranges are allocated when allocating the primary range and are propagated 1:1 to the user namespace. We track delegated ranges in ".delegate" files in the userns registry so that they can't be used for other range allocations. We make one exception for delegated ranges though, if we get a request from a user namespace that is a child of the user namespace that owns the delegated ranges, we allow allocating from the delegated range. The parent userns already has full ownership over the child userns, so it doesn't matter that the parent userns and the child userns share the same range. This allows making use of delegated ranges without having to run another copy of nsresourced inside the parent userns to hand out from the delegated range. To support recursive delegations, we keep track of the previous owners of the delegated range and restore ownership to the last previous owner when the current owner is freed.
This commit is contained in:
committed by
Daan De Meyer
parent
6c2d437585
commit
025439faaa
@@ -195,4 +195,306 @@ TEST(uid_range_coalesce) {
|
||||
ASSERT_EQ(p->entries[0].nr, 115U);
|
||||
}
|
||||
|
||||
TEST(uid_range_clip) {
|
||||
_cleanup_(uid_range_freep) UIDRange *p = NULL;
|
||||
|
||||
/* Build a range: 100-199, 300-399, 500-599 */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "300-399"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "500-599"));
|
||||
ASSERT_EQ(uid_range_entries(p), 3U);
|
||||
|
||||
/* Intersect with range that covers all entries */
|
||||
ASSERT_OK(uid_range_clip(p, 0, 1000));
|
||||
ASSERT_EQ(uid_range_entries(p), 3U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
ASSERT_EQ(p->entries[1].start, 300U);
|
||||
ASSERT_EQ(p->entries[1].nr, 100U);
|
||||
ASSERT_EQ(p->entries[2].start, 500U);
|
||||
ASSERT_EQ(p->entries[2].nr, 100U);
|
||||
|
||||
/* Intersect with range that excludes first and last entries */
|
||||
ASSERT_OK(uid_range_clip(p, 200, 499));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 300U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Test partial overlap - trimming from both sides */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_clip(p, 150, 180));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 150U);
|
||||
ASSERT_EQ(p->entries[0].nr, 31U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Test intersection that removes all entries */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_clip(p, 500, 600));
|
||||
ASSERT_TRUE(uid_range_is_empty(p));
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Test invalid min > max */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_ERROR(uid_range_clip(p, 200, 100), EINVAL);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Test with max == UINT32_MAX (should not overflow) */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_clip(p, 0, UINT32_MAX));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Test with both min and max at extremes */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "500-599"));
|
||||
ASSERT_OK(uid_range_clip(p, 150, UINT32_MAX));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_EQ(p->entries[0].start, 150U);
|
||||
ASSERT_EQ(p->entries[0].nr, 50U);
|
||||
ASSERT_EQ(p->entries[1].start, 500U);
|
||||
ASSERT_EQ(p->entries[1].nr, 100U);
|
||||
}
|
||||
|
||||
TEST(uid_range_partition) {
|
||||
_cleanup_(uid_range_freep) UIDRange *p = NULL;
|
||||
|
||||
/* Single entry that divides evenly */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-299"));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_OK(uid_range_partition(p, 100));
|
||||
ASSERT_EQ(uid_range_entries(p), 3U);
|
||||
ASSERT_EQ(p->entries[0].start, 0U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
ASSERT_EQ(p->entries[1].start, 100U);
|
||||
ASSERT_EQ(p->entries[1].nr, 100U);
|
||||
ASSERT_EQ(p->entries[2].start, 200U);
|
||||
ASSERT_EQ(p->entries[2].nr, 100U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Entry with remainder (gets truncated) */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-249"));
|
||||
ASSERT_OK(uid_range_partition(p, 100));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_EQ(p->entries[0].start, 0U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
ASSERT_EQ(p->entries[1].start, 100U);
|
||||
ASSERT_EQ(p->entries[1].nr, 100U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Entry smaller than partition size - gets dropped */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-49"));
|
||||
ASSERT_OK(uid_range_partition(p, 100));
|
||||
ASSERT_TRUE(uid_range_is_empty(p));
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Multiple entries */
|
||||
ASSERT_OK(uid_range_add_str(&p, "0-199"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "1000-1299"));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_OK(uid_range_partition(p, 100));
|
||||
ASSERT_EQ(uid_range_entries(p), 5U);
|
||||
ASSERT_EQ(p->entries[0].start, 0U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
ASSERT_EQ(p->entries[1].start, 100U);
|
||||
ASSERT_EQ(p->entries[1].nr, 100U);
|
||||
ASSERT_EQ(p->entries[2].start, 1000U);
|
||||
ASSERT_EQ(p->entries[2].nr, 100U);
|
||||
ASSERT_EQ(p->entries[3].start, 1100U);
|
||||
ASSERT_EQ(p->entries[3].nr, 100U);
|
||||
ASSERT_EQ(p->entries[4].start, 1200U);
|
||||
ASSERT_EQ(p->entries[4].nr, 100U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Partition size of 1 */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-102"));
|
||||
ASSERT_OK(uid_range_partition(p, 1));
|
||||
ASSERT_EQ(uid_range_entries(p), 3U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 1U);
|
||||
ASSERT_EQ(p->entries[1].start, 101U);
|
||||
ASSERT_EQ(p->entries[1].nr, 1U);
|
||||
ASSERT_EQ(p->entries[2].start, 102U);
|
||||
ASSERT_EQ(p->entries[2].nr, 1U);
|
||||
}
|
||||
|
||||
TEST(uid_range_copy) {
|
||||
_cleanup_(uid_range_freep) UIDRange *p = NULL, *copy = NULL;
|
||||
|
||||
/* Copy NULL range */
|
||||
ASSERT_OK(uid_range_copy(NULL, ©));
|
||||
ASSERT_TRUE(uid_range_is_empty(copy));
|
||||
|
||||
copy = uid_range_free(copy);
|
||||
|
||||
/* Copy empty range */
|
||||
p = new0(UIDRange, 1);
|
||||
ASSERT_NOT_NULL(p);
|
||||
ASSERT_OK(uid_range_copy(p, ©));
|
||||
ASSERT_NOT_NULL(copy);
|
||||
ASSERT_TRUE(uid_range_is_empty(copy));
|
||||
|
||||
p = uid_range_free(p);
|
||||
copy = uid_range_free(copy);
|
||||
|
||||
/* Copy range with entries */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "300-399"));
|
||||
ASSERT_OK(uid_range_copy(p, ©));
|
||||
ASSERT_TRUE(uid_range_equal(p, copy));
|
||||
|
||||
/* Verify it's a deep copy - modifying original doesn't affect copy */
|
||||
ASSERT_OK(uid_range_add_str(&p, "500-599"));
|
||||
ASSERT_FALSE(uid_range_equal(p, copy));
|
||||
ASSERT_EQ(uid_range_entries(copy), 2U);
|
||||
}
|
||||
|
||||
TEST(uid_range_remove) {
|
||||
_cleanup_(uid_range_freep) UIDRange *p = NULL;
|
||||
|
||||
/* Build a range: 100-199 */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
|
||||
/* Remove with size 0 - no-op */
|
||||
ASSERT_OK(uid_range_remove(p, 150, 0));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
|
||||
/* Remove range that doesn't overlap - no change */
|
||||
ASSERT_OK(uid_range_remove(p, 0, 50));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
|
||||
ASSERT_OK(uid_range_remove(p, 300, 50));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 100U);
|
||||
|
||||
/* Remove from the start of the entry */
|
||||
ASSERT_OK(uid_range_remove(p, 100, 10));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 110U);
|
||||
ASSERT_EQ(p->entries[0].nr, 90U);
|
||||
|
||||
/* Remove from the end of the entry */
|
||||
ASSERT_OK(uid_range_remove(p, 190, 10));
|
||||
ASSERT_EQ(uid_range_entries(p), 1U);
|
||||
ASSERT_EQ(p->entries[0].start, 110U);
|
||||
ASSERT_EQ(p->entries[0].nr, 80U);
|
||||
|
||||
/* Remove from the middle - splits the entry */
|
||||
ASSERT_OK(uid_range_remove(p, 140, 20));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_EQ(p->entries[0].start, 110U);
|
||||
ASSERT_EQ(p->entries[0].nr, 30U);
|
||||
ASSERT_EQ(p->entries[1].start, 160U);
|
||||
ASSERT_EQ(p->entries[1].nr, 30U);
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Remove entire entry */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_remove(p, 100, 100));
|
||||
ASSERT_TRUE(uid_range_is_empty(p));
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Remove range larger than entry */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_remove(p, 50, 200));
|
||||
ASSERT_TRUE(uid_range_is_empty(p));
|
||||
|
||||
p = uid_range_free(p);
|
||||
|
||||
/* Remove affecting multiple entries */
|
||||
ASSERT_OK(uid_range_add_str(&p, "100-199"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "300-399"));
|
||||
ASSERT_OK(uid_range_add_str(&p, "500-599"));
|
||||
ASSERT_EQ(uid_range_entries(p), 3U);
|
||||
|
||||
/* Remove range spanning the middle entry completely and trimming others */
|
||||
ASSERT_OK(uid_range_remove(p, 150, 400));
|
||||
ASSERT_EQ(uid_range_entries(p), 2U);
|
||||
ASSERT_EQ(p->entries[0].start, 100U);
|
||||
ASSERT_EQ(p->entries[0].nr, 50U);
|
||||
ASSERT_EQ(p->entries[1].start, 550U);
|
||||
ASSERT_EQ(p->entries[1].nr, 50U);
|
||||
}
|
||||
|
||||
TEST(uid_range_translate) {
|
||||
_cleanup_(uid_range_freep) UIDRange *o = NULL, *i = NULL;
|
||||
uid_t uid;
|
||||
|
||||
ASSERT_OK(uid_range_add_str_full(&o, "200-299", /* coalesce= */ false));
|
||||
ASSERT_OK(uid_range_add_str_full(&i, "100-199", /* coalesce= */ false));
|
||||
ASSERT_OK(uid_range_translate(o, i, 250, &uid));
|
||||
ASSERT_EQ(uid, 150U);
|
||||
ASSERT_OK(uid_range_translate(i, o, 150, &uid));
|
||||
ASSERT_EQ(uid, 250U);
|
||||
|
||||
ASSERT_OK(uid_range_add_str_full(&o, "300-399", /* coalesce= */ false));
|
||||
ASSERT_OK(uid_range_add_str_full(&i, "350-449", /* coalesce= */ false));
|
||||
ASSERT_OK(uid_range_translate(o, i, 350, &uid));
|
||||
ASSERT_EQ(uid, 400U);
|
||||
ASSERT_OK(uid_range_translate(i, o, 400, &uid));
|
||||
ASSERT_EQ(uid, 350U);
|
||||
|
||||
/* Test translating at range boundaries */
|
||||
ASSERT_OK(uid_range_translate(o, i, 200, &uid));
|
||||
ASSERT_EQ(uid, 100U);
|
||||
ASSERT_OK(uid_range_translate(o, i, 299, &uid));
|
||||
ASSERT_EQ(uid, 199U);
|
||||
ASSERT_OK(uid_range_translate(o, i, 300, &uid));
|
||||
ASSERT_EQ(uid, 350U);
|
||||
ASSERT_OK(uid_range_translate(o, i, 399, &uid));
|
||||
ASSERT_EQ(uid, 449U);
|
||||
|
||||
/* Test reverse translation at boundaries */
|
||||
ASSERT_OK(uid_range_translate(i, o, 100, &uid));
|
||||
ASSERT_EQ(uid, 200U);
|
||||
ASSERT_OK(uid_range_translate(i, o, 199, &uid));
|
||||
ASSERT_EQ(uid, 299U);
|
||||
ASSERT_OK(uid_range_translate(i, o, 350, &uid));
|
||||
ASSERT_EQ(uid, 300U);
|
||||
ASSERT_OK(uid_range_translate(i, o, 449, &uid));
|
||||
ASSERT_EQ(uid, 399U);
|
||||
|
||||
/* Test UID not in any range returns ESRCH */
|
||||
ASSERT_ERROR(uid_range_translate(o, i, 0, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(o, i, 199, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(o, i, 400, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(i, o, 0, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(i, o, 99, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(i, o, 200, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(i, o, 349, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(i, o, 450, &uid), ESRCH);
|
||||
|
||||
o = uid_range_free(o);
|
||||
i = uid_range_free(i);
|
||||
|
||||
/* Test with single-element ranges */
|
||||
ASSERT_OK(uid_range_add_str_full(&o, "1000", /* coalesce= */ false));
|
||||
ASSERT_OK(uid_range_add_str_full(&i, "5000", /* coalesce= */ false));
|
||||
ASSERT_OK(uid_range_translate(o, i, 1000, &uid));
|
||||
ASSERT_EQ(uid, 5000U);
|
||||
ASSERT_OK(uid_range_translate(i, o, 5000, &uid));
|
||||
ASSERT_EQ(uid, 1000U);
|
||||
ASSERT_ERROR(uid_range_translate(o, i, 999, &uid), ESRCH);
|
||||
ASSERT_ERROR(uid_range_translate(o, i, 1001, &uid), ESRCH);
|
||||
}
|
||||
|
||||
DEFINE_TEST_MAIN(LOG_DEBUG);
|
||||
|
||||
Reference in New Issue
Block a user