From 618f2234918e302f538b7c48ab5c78f986401354 Mon Sep 17 00:00:00 2001 From: Albert S Date: Mon, 26 Dec 2022 16:38:17 +0100 Subject: [PATCH] enter_namespaces(): Fix uid/gid mapping This was not caught before because a test was missing, fprintf() without ferror() didn't help, and calling code did not depend on uid maps so far. Add tests. --- exile.c | 61 +++++++++++++++++++--------------- test.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 135 insertions(+), 26 deletions(-) diff --git a/exile.c b/exile.c index 157779c..469ab33 100644 --- a/exile.c +++ b/exile.c @@ -942,6 +942,11 @@ static int enter_namespaces(int namespace_options) { if(namespace_options & EXILE_UNSHARE_USER) { + uid_t current_uid = getuid(); + gid_t current_gid = getgid(); + + char buf[1024] = {0}; + int ret = unshare(CLONE_NEWUSER); if(ret == -1) { @@ -949,47 +954,51 @@ static int enter_namespaces(int namespace_options) return ret; } - uid_t current_uid = getuid(); - gid_t current_gid = getgid(); + int fd = open("/proc/self/setgroups", O_WRONLY); + if(fd == -1) + { + EXILE_LOG_ERROR("Failed to open /proc/self/setgroups for writing"); + return -1; + } + int writesize = snprintf(buf, sizeof(buf), "deny"); + int writeret = write(fd, buf, writesize); + if(writeret < 0 || writeret < writesize) + { + EXILE_LOG_ERROR("Failed to write to /proc/self/setgroups: %i (%s)\n", writeret, strerror(errno)); + return -1; + } + close(fd); - FILE *fp = fopen("/proc/self/setgroups", "w"); - if(fp == NULL) + fd = open("/proc/self/uid_map", O_WRONLY); + if(fd == -1) { - EXILE_LOG_ERROR("fopen failed while trying to deny setgroups\n"); + EXILE_LOG_ERROR("Failed to open /proc/self/uid_map for writing"); return -1; } - if(fprintf(fp, "deny") < 0) + writesize = snprintf(buf, sizeof(buf), "0 %u 1\n", current_uid); + writeret = write(fd, buf, writesize); + if(writeret < 0 || writeret < writesize) { - EXILE_LOG_ERROR("fprintf failed while trying to write setgroups\n"); + EXILE_LOG_ERROR("Failed to write to /proc/self/uid_map: %i (%s)\n", writeret, strerror(errno)); return -1; } - fclose(fp); + close(fd); - fp = fopen("/proc/self/uid_map", "w"); - if(fp == NULL) - { - EXILE_LOG_ERROR("fopen failed while trying to write uid_map\n"); - return -1; - } - if(fprintf(fp, "0 %i", current_uid) < 0) - { - EXILE_LOG_ERROR("fprintf failed while trying to write uid_map\n"); - return -1; - } - fclose(fp); - fp = fopen("/proc/self/gid_map", "w"); - if(fp == NULL) + fd = open("/proc/self/gid_map", O_WRONLY); + if(fd == -1) { - EXILE_LOG_ERROR("fopen failed while trying to write gid_map\n"); + EXILE_LOG_ERROR("Failed to open /proc/self/gid_map for writing"); return -1; } - if(fprintf(fp, "0 %i", current_gid) < 0) + writesize = snprintf(buf, sizeof(buf), "0 %u 1\n", current_gid); + writeret = write(fd, buf, writesize); + if(writeret < 0 || writeret < writesize) { - EXILE_LOG_ERROR("fprintf failed while trying to write gid_map\n"); + EXILE_LOG_ERROR("Failed to write to /proc/self/gid_map: %i (%s)\n", writeret, strerror(errno)); return -1; } - fclose(fp); + close(fd); } if(namespace_options & EXILE_UNSHARE_MOUNT) diff --git a/test.c b/test.c index d1e0dd5..4a4c0be 100644 --- a/test.c +++ b/test.c @@ -661,6 +661,104 @@ int test_clone3_nosys() return 0; } +int do_test_nsuidmap(const char *path, const char *firstfield, const char *secondfield, const char *thirdfield) +{ + char *line = NULL; + size_t n = 0; + FILE *fp = fopen(path, "r"); + + int ret = getdelim(&line, &n, ' ', fp); + while(ret != -1 && strlen(line) == 1 && *line == ' ') + ret = getdelim(&line, &n, ' ', fp); + if(ret == -1) + { + LOG("getdelim() failed to read a line from %s\n", path); + return 1; + } + line[ret-1] = '\0'; + if(strcmp(line, firstfield) != 0) + { + LOG("Invalid value for first entry in map: Expected: %s, was: %s\n", firstfield, line); + return 1; + } + + ret = getdelim(&line, &n, ' ', fp); + while(ret != -1 && strlen(line) == 1 && *line == ' ') + ret = getdelim(&line, &n, ' ', fp); + if(ret == -1) + { + LOG("getdelim() failed to read a line from map\n"); + return 1; + } + line[ret-1] = '\0'; + + if(strcmp(line, secondfield) != 0) + { + LOG("Invalid value for second entry in map: Expected: %s, was: %s\n", secondfield, line); + return 1; + } + + + ret = getdelim(&line, &n, ' ', fp); + while(ret != -1 && strlen(line) == 1 && *line == ' ') + ret = getdelim(&line, &n, ' ', fp); + if(ret == -1) + { + LOG("getdelim() failed to read a line from uid_map\n"); + return 1; + } + line[ret-1] = '\0'; + if(strcmp(line, thirdfield) != 0) + { + LOG("Invalid value for second entry in map: Expected: %s, was: %s\n", thirdfield, line); + return 1; + } + + fclose(fp); + return 0; +} + +int test_unshare_user() +{ + char uidstr[64]; + snprintf(uidstr, sizeof(uidstr), "%u", getuid()); + + char gidstr[64]; + snprintf(gidstr, sizeof(gidstr), "%u", getgid()); + + struct exile_policy *policy = exile_init_policy(); + policy->namespace_options = EXILE_UNSHARE_USER; + xexile_enable_policy(policy); + + if(do_test_nsuidmap("/proc/self/uid_map", "0", uidstr, "1") != 0) + { + LOG("/proc/self/uid_map failed\n"); + return 1; + } + + if(do_test_nsuidmap("/proc/self/gid_map", "0", gidstr, "1") != 0) + { + LOG("/proc/self/gid_map failed\n"); + return 1; + } + + FILE *fp = fopen("/proc/self/setgroups", "r"); + + char buffer[4096] = { 0 }; + fread(buffer, sizeof(buffer), 1, fp); + fclose(fp); + + if(strcmp(buffer, "deny\n") != 0) + { + LOG("/proc/self/setgroups does not contain 'deny'\n"); + return 1; + } + + return 0; + + +} + struct dispatcher { char *name; @@ -689,6 +787,8 @@ struct dispatcher dispatchers[] = { { "launch-get", &test_launch_get}, { "vow_from_str", &test_vows_from_str}, { "clone3_nosys", &test_clone3_nosys}, + { "unshare-user", &test_unshare_user}, + }; int main(int argc, char *argv[])