Skip to content

Commit ff09fe5

Browse files
DaanDeMeyerbluca
authored andcommitted
test: Gracefully handle running within user namespace with single user
Unprivileged users often make themselves root by unsharing a user namespace and then mapping their current user to root which does not require privileges. Let's make sure our tests don't fail in such an environment by adding checks where required to see if we're not running in a user namespace with only a single user. (cherry picked from commit ef31767) (cherry picked from commit ec5cdf9) (cherry picked from commit 4d4513c) (cherry picked from commit 1c514e7)
1 parent 35628ef commit ff09fe5

File tree

9 files changed

+37
-8
lines changed

9 files changed

+37
-8
lines changed

src/shared/tests.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include "strv.h"
2929
#include "tests.h"
3030
#include "tmpfile-util.h"
31+
#include "uid-range.h"
3132

3233
char* setup_fake_runtime_dir(void) {
3334
char t[] = "/tmp/fake-xdg-runtime-XXXXXX", *p;
@@ -165,6 +166,20 @@ bool have_namespaces(void) {
165166
assert_not_reached();
166167
}
167168

169+
bool userns_has_single_user(void) {
170+
_cleanup_(uid_range_freep) UidRange *uidrange = NULL;
171+
172+
/* Check if we're in a user namespace with only a single user mapped in. We special case this
173+
* scenario in a few tests because it's the only kind of namespace that can be created unprivileged
174+
* and as such happens more often than not, so we make sure to deal with it so that all tests pass
175+
* in such environments. */
176+
177+
if (uid_range_load_userns(&uidrange, NULL) < 0)
178+
return false;
179+
180+
return uidrange->n_entries == 1 && uidrange->entries[0].nr == 1;
181+
}
182+
168183
bool can_memlock(void) {
169184
/* Let's see if we can mlock() a larger blob of memory. BPF programs are charged against
170185
* RLIMIT_MEMLOCK, hence let's first make sure we can lock memory at all, and skip the test if we

src/shared/tests.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ void test_setup_logging(int level);
4848
int write_tmpfile(char *pattern, const char *contents);
4949

5050
bool have_namespaces(void);
51+
bool userns_has_single_user(void);
5152

5253
/* We use the small but non-trivial limit here */
5354
#define CAN_MEMLOCK_SIZE (512 * 1024U)

src/test/test-acl-util.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ TEST_RET(add_acls_for_user) {
3434
cmd = strjoina("getfacl -p ", fn);
3535
assert_se(system(cmd) == 0);
3636

37-
if (getuid() == 0) {
37+
if (getuid() == 0 && !userns_has_single_user()) {
3838
const char *nobody = NOBODY_USER_NAME;
3939
r = get_user_creds(&nobody, &uid, NULL, NULL, NULL, 0);
4040
if (r < 0)

src/test/test-capability.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -267,10 +267,13 @@ int main(int argc, char *argv[]) {
267267

268268
show_capabilities();
269269

270-
test_drop_privileges();
270+
if (!userns_has_single_user())
271+
test_drop_privileges();
272+
271273
test_update_inherited_set();
272274

273-
fork_test(test_have_effective_cap);
275+
if (!userns_has_single_user())
276+
fork_test(test_have_effective_cap);
274277

275278
if (run_ambient)
276279
fork_test(test_apply_ambient_caps);

src/test/test-chown-rec.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,8 @@ TEST(chown_recursive) {
153153
}
154154

155155
static int intro(void) {
156-
if (geteuid() != 0)
157-
return log_tests_skipped("not running as root");
156+
if (geteuid() != 0 || userns_has_single_user())
157+
return log_tests_skipped("not running as root or in userns with single user");
158158

159159
return EXIT_SUCCESS;
160160
}

src/test/test-condition.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -994,6 +994,13 @@ TEST(condition_test_group) {
994994
condition_free(condition);
995995
free(gid);
996996

997+
/* In an unprivileged user namespace with the current user mapped to root, all the auxiliary groups
998+
* of the user will be mapped to the nobody group, which means the user in the user namespace is in
999+
* both the root and the nobody group, meaning the next test can't work, so let's skip it in that
1000+
* case. */
1001+
if (in_group(NOBODY_GROUP_NAME) && in_group("root"))
1002+
return (void) log_tests_skipped("user is in both root and nobody group");
1003+
9971004
groupname = (char*)(getegid() == 0 ? NOBODY_GROUP_NAME : "root");
9981005
condition = condition_new(CONDITION_GROUP, groupname, false, false);
9991006
assert_se(condition);

src/test/test-fs-util.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -826,8 +826,8 @@ TEST(chmod_and_chown) {
826826
struct stat st;
827827
const char *p;
828828

829-
if (geteuid() != 0)
830-
return;
829+
if (geteuid() != 0 || userns_has_single_user())
830+
return (void) log_tests_skipped("not running as root or in userns with single user");
831831

832832
BLOCK_WITH_UMASK(0000);
833833

src/test/test-rm-rf.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,9 @@ static void test_rm_rf_chmod_inner(void) {
8989
TEST(rm_rf_chmod) {
9090
int r;
9191

92+
if (getuid() == 0 && userns_has_single_user())
93+
return (void) log_tests_skipped("running as root or in userns with single user");
94+
9295
if (getuid() == 0) {
9396
/* This test only works unpriv (as only then the access mask for the owning user matters),
9497
* hence drop privs here */

src/test/test-socket-util.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,7 @@ TEST(getpeercred_getpeergroups) {
170170
struct ucred ucred;
171171
int pair[2];
172172

173-
if (geteuid() == 0) {
173+
if (geteuid() == 0 && !userns_has_single_user()) {
174174
test_uid = 1;
175175
test_gid = 2;
176176
test_gids = (gid_t*) gids;

0 commit comments

Comments
 (0)