Message ID | 20210712162208.2396-1-rpalethorpe@suse.com |
---|---|
State | Changes Requested |
Headers | show |
Series | userns08, CVE-2018-18955: Broken id mappings in nested namespaces | expand |
Hi! > +#include "tst_test.h" > +#include "tst_clone.h" > +#include "lapi/clone.h" > +#include "tst_safe_file_at.h" > + > +static pid_t clone_newuser(void) > +{ > + const struct tst_clone_args cargs = { > + CLONE_NEWUSER, > + SIGCHLD }; I would have put the closing curly brace on a separate line here, but that is very minor. > + return SAFE_CLONE(&cargs); > +} > + > +static void write_mapping(const pid_t proc_in_ns, > + const char *const id_mapping) > +{ > + char proc_path[PATH_MAX]; > + int proc_dir; > + > + sprintf(proc_path, "/proc/%d", (int)proc_in_ns); > + proc_dir = SAFE_OPEN(proc_path, O_DIRECTORY); > + > + TEST(faccessat(proc_dir, "uid_map", F_OK, 0)); > + if (TST_RET && TST_ERR == ENOENT) > + tst_brk(TCONF, "No uid_map file; interface was added in v3.5"); > + > + SAFE_FILE_PRINTFAT(proc_dir, "setgroups", "%s", "deny"); > + SAFE_FILE_PRINTFAT(proc_dir, "uid_map", "%s", id_mapping); > + SAFE_FILE_PRINTFAT(proc_dir, "gid_map", "%s", id_mapping); > + > + SAFE_CLOSE(proc_dir); > +} > + > +static void ns_level2(void) > +{ > + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) > + tst_res(TINFO | TERRNO, "Failed to set dumpable flag"); > + TST_CHECKPOINT_WAKE(20); > + TST_CHECKPOINT_WAIT(21); We do have TST_CHECKPOINT_WAKE_AND_WAIT() especially for this purpose. Also the id passed to checkpoints is really offset to a memory, so it makes sense to allocate them starting at 0 and increase by one. It's not a big problem as long as the numbers are small enough, we have nearly whole page for checkpoints, so everything should work fine as long as the numbers are less than 1000 at the moment, but it may become one if we attempt to stuff more test library stuff into the shared page later on. > + TST_EXP_FAIL(open("restricted", O_WRONLY), EACCES, > + "Denied write access to ./restricted"); > + > + exit(0); > +} > + > +static void ns_level1(void) > +{ > + const char *const map_over_5 = "0 0 1\n1 1 1\n2 2 1\n3 3 1\n4 4 1\n5 5 990"; > + pid_t level2_proc; > + > + TST_CHECKPOINT_WAIT(10); > + > + SAFE_SETGID(0); > + SAFE_SETUID(0); > + > + level2_proc = clone_newuser(); > + if (!level2_proc) > + ns_level2(); > + > + TST_CHECKPOINT_WAIT(20); > + > + write_mapping(level2_proc, map_over_5); > + > + TST_CHECKPOINT_WAKE(21); > + tst_reap_children(); > + > + exit(0); > +} > + > +static void run(void) > +{ > + pid_t level1_proc; > + > + SAFE_SETEGID(100000); > + SAFE_SETEUID(100000); > + > + level1_proc = clone_newuser(); > + if (!level1_proc) > + ns_level1(); > + > + SAFE_SETEGID(0); > + SAFE_SETEUID(0); > + > + write_mapping(level1_proc, "0 100000 1000"); > + > + TST_CHECKPOINT_WAKE(10); > + tst_reap_children(); > +} > + > +static void setup(void) > +{ > + int fd = SAFE_OPEN("restricted", O_CREAT, 0700); ^ huh no |O_WRONLY? > + SAFE_WRITE(fd, 1, "\n", 1); > + SAFE_CLOSE(fd); > +} > + > +static struct tst_test test = { > + .setup = setup, > + .test_all = run, > + .needs_checkpoints = 1, > + .needs_tmpdir = 1, > + .needs_root = 1, > + .forks_child = 1, > + .needs_kconfigs = (const char *[]) { > + "CONFIG_USER_NS", > + NULL > + }, > + .tags = (const struct tst_tag[]) { > + {"linux-git", "d2f007dbe7e4"}, > + {"CVE", "CVE-2018-18955"}, > + {} > + }, > +}; > -- > 2.31.1 > > > -- > Mailing list info: https://lists.linux.it/listinfo/ltp
diff --git a/include/lapi/clone.h b/include/lapi/clone.h index 81db443c9..0d49c97f4 100644 --- a/include/lapi/clone.h +++ b/include/lapi/clone.h @@ -37,6 +37,10 @@ static inline int clone3(struct clone_args *args, size_t size) #define CLONE_PIDFD 0x00001000 /* set if a pidfd should be placed in parent */ #endif +#ifndef CLONE_NEWUSER +# define CLONE_NEWUSER 0x10000000 +#endif + static inline void clone3_supported_by_kernel(void) { if ((tst_kvercmp(5, 3, 0)) < 0) { diff --git a/runtest/containers b/runtest/containers index 276096709..eea7bfadb 100644 --- a/runtest/containers +++ b/runtest/containers @@ -85,6 +85,7 @@ userns04 userns04 userns05 userns05 userns06 userns06 userns07 userns07 +userns08 userns08 # time namespaces sysinfo03 sysinfo03 diff --git a/runtest/cve b/runtest/cve index 5a6ef966d..d2c7577ca 100644 --- a/runtest/cve +++ b/runtest/cve @@ -55,6 +55,7 @@ cve-2018-1000204 ioctl_sg01 cve-2018-12896 timer_settime03 cve-2018-18445 bpf_prog04 cve-2018-18559 bind06 +cve-2018-18955 userns08 cve-2018-19854 crypto_user01 cve-2019-8912 af_alg07 cve-2020-11494 pty04 diff --git a/testcases/kernel/containers/.gitignore b/testcases/kernel/containers/.gitignore index 7dc2608f3..5c2b90312 100644 --- a/testcases/kernel/containers/.gitignore +++ b/testcases/kernel/containers/.gitignore @@ -11,3 +11,4 @@ userns/userns05 userns/userns06_capcheck userns/userns06 userns/userns07 +userns/userns08 diff --git a/testcases/kernel/containers/userns/userns08.c b/testcases/kernel/containers/userns/userns08.c new file mode 100644 index 000000000..a0900627d --- /dev/null +++ b/testcases/kernel/containers/userns/userns08.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 SUSE LLC <rpalethorpe@suse.com> + */ + +/*\ + * [Description] + * + * Reproducer of CVE-2018-18955; broken uid/gid mapping for nested + * user namespaces with >5 ranges + * + * See original reproducer and description by Jan Horn: + * https://bugs.chromium.org/p/project-zero/issues/detail?id=1712 + * + * Note that calling seteuid from root can cause the dumpable bit to + * be unset. The proc files of non dumpable processes are then owned + * by (the real) root. So on the second level we reset dumpable to 1. + * + */ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <sys/prctl.h> +#include <sys/mount.h> + +#include "tst_test.h" +#include "tst_clone.h" +#include "lapi/clone.h" +#include "tst_safe_file_at.h" + +static pid_t clone_newuser(void) +{ + const struct tst_clone_args cargs = { + CLONE_NEWUSER, + SIGCHLD }; + + return SAFE_CLONE(&cargs); +} + +static void write_mapping(const pid_t proc_in_ns, + const char *const id_mapping) +{ + char proc_path[PATH_MAX]; + int proc_dir; + + sprintf(proc_path, "/proc/%d", (int)proc_in_ns); + proc_dir = SAFE_OPEN(proc_path, O_DIRECTORY); + + TEST(faccessat(proc_dir, "uid_map", F_OK, 0)); + if (TST_RET && TST_ERR == ENOENT) + tst_brk(TCONF, "No uid_map file; interface was added in v3.5"); + + SAFE_FILE_PRINTFAT(proc_dir, "setgroups", "%s", "deny"); + SAFE_FILE_PRINTFAT(proc_dir, "uid_map", "%s", id_mapping); + SAFE_FILE_PRINTFAT(proc_dir, "gid_map", "%s", id_mapping); + + SAFE_CLOSE(proc_dir); +} + +static void ns_level2(void) +{ + if (prctl(PR_SET_DUMPABLE, 1, 0, 0, 0)) + tst_res(TINFO | TERRNO, "Failed to set dumpable flag"); + TST_CHECKPOINT_WAKE(20); + TST_CHECKPOINT_WAIT(21); + + TST_EXP_FAIL(open("restricted", O_WRONLY), EACCES, + "Denied write access to ./restricted"); + + exit(0); +} + +static void ns_level1(void) +{ + const char *const map_over_5 = "0 0 1\n1 1 1\n2 2 1\n3 3 1\n4 4 1\n5 5 990"; + pid_t level2_proc; + + TST_CHECKPOINT_WAIT(10); + + SAFE_SETGID(0); + SAFE_SETUID(0); + + level2_proc = clone_newuser(); + if (!level2_proc) + ns_level2(); + + TST_CHECKPOINT_WAIT(20); + + write_mapping(level2_proc, map_over_5); + + TST_CHECKPOINT_WAKE(21); + tst_reap_children(); + + exit(0); +} + +static void run(void) +{ + pid_t level1_proc; + + SAFE_SETEGID(100000); + SAFE_SETEUID(100000); + + level1_proc = clone_newuser(); + if (!level1_proc) + ns_level1(); + + SAFE_SETEGID(0); + SAFE_SETEUID(0); + + write_mapping(level1_proc, "0 100000 1000"); + + TST_CHECKPOINT_WAKE(10); + tst_reap_children(); +} + +static void setup(void) +{ + int fd = SAFE_OPEN("restricted", O_CREAT, 0700); + + SAFE_WRITE(fd, 1, "\n", 1); + SAFE_CLOSE(fd); +} + +static struct tst_test test = { + .setup = setup, + .test_all = run, + .needs_checkpoints = 1, + .needs_tmpdir = 1, + .needs_root = 1, + .forks_child = 1, + .needs_kconfigs = (const char *[]) { + "CONFIG_USER_NS", + NULL + }, + .tags = (const struct tst_tag[]) { + {"linux-git", "d2f007dbe7e4"}, + {"CVE", "CVE-2018-18955"}, + {} + }, +};
Signed-off-by: Richard Palethorpe <rpalethorpe@suse.com> --- include/lapi/clone.h | 4 + runtest/containers | 1 + runtest/cve | 1 + testcases/kernel/containers/.gitignore | 1 + testcases/kernel/containers/userns/userns08.c | 141 ++++++++++++++++++ 5 files changed, 148 insertions(+) create mode 100644 testcases/kernel/containers/userns/userns08.c