From a135fc1eca0477b446159f2a546a51e9f40222fa Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Sun, 26 Nov 2023 18:53:18 +0000 Subject: [PATCH 01/16] Add small unprivileged chroot --- sysa/after_wrap.kaem | 10 ++++ sysa/wrap.c | 116 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 sysa/after_wrap.kaem create mode 100644 sysa/wrap.c diff --git a/sysa/after_wrap.kaem b/sysa/after_wrap.kaem new file mode 100644 index 00000000..4bfca595 --- /dev/null +++ b/sysa/after_wrap.kaem @@ -0,0 +1,10 @@ +#!/bin/sh + +PATH=./${ARCH_DIR}/bin + +./${ARCH_DIR}/bin/M2-Mesoplanet --architecture ${ARCH} \ + -f sysa/wrap.c \ + -o ./${ARCH_DIR}/bin/wrap \ + --temp-directory ./x86/artifact + +exec ./${ARCH_DIR}/bin/wrap ./${ARCH_DIR}/bin/kaem --verbose --strict --file sysa/after.kaem diff --git a/sysa/wrap.c b/sysa/wrap.c new file mode 100644 index 00000000..048aa699 --- /dev/null +++ b/sysa/wrap.c @@ -0,0 +1,116 @@ +#define CLONE_NEWUSER 0x10000000 +#define CLONE_NEWNS 0x00020000 +#define MS_BIND 4096 + +#include +#include +#include + +#include + +int unshare(int flags) { + asm ( + "lea_ebx,[esp+DWORD] %4" + "mov_ebx,[ebx]" + "mov_eax, %310" + "int !0x80" + ); +} + +int geteuid() { + asm ( + "mov_eax, %201" + "int !0x80" + ); +} + +int getegid() { + asm ( + "mov_eax, %202" + "int !0x80" + ); +} + +int mount( + char *source, char *target, char *filesystemtype, + unsigned mountflags, void *data +) { + asm ( + "DEFINE mov_esi,[esp+DWORD] 8BB424" + "DEFINE mov_edi,[esp+DWORD] 8BBC24" + "lea_ebx,[esp+DWORD] %20" + "mov_ebx,[ebx]" + "lea_ecx,[esp+DWORD] %16" + "mov_ecx,[ecx]" + "lea_edx,[esp+DWORD] %12" + "mov_edx,[edx]" + "mov_esi,[esp+DWORD] %8 ; mov esi, [esp+8]" + "mov_edi,[esp+DWORD] %4 ; mov edi, [esp+4]" + "mov_eax, %21" + "int !0x80" + ); +} + +int chroot(char *path) { + asm ( + "lea_ebx,[esp+DWORD] %4" + "mov_ebx,[ebx]" + "mov_eax, %61" + "int !0x80" + ); +} + +int set_map(int parent_id, char *path) { + char *map_contents; + char *parent_id_str; + int fd = open(path, O_WRONLY, 0); + require(fd != -1, "Cannot open map file"); + + map_contents = calloc(37, sizeof(char)); + + strcpy(map_contents, "0 "); + parent_id_str = int2str(parent_id, 10, 0); + strcat(map_contents, parent_id_str); + strcat(map_contents, " 1"); + write(fd, map_contents, strlen(map_contents)); + write(STDOUT_FILENO, map_contents, strlen(map_contents)); + free(map_contents); + close(fd); +} + +void touch(char *path) { + int fd = open(path, O_CREAT, 0777); + require(fd != -1, "Cannot open file"); + close(fd); +} + +void deny_setgroups() { + int fd = open("/proc/self/setgroups", O_WRONLY, 0777); + require(fd != -1, "Failed to open /proc/self/setgroups"); + write(fd, "deny", 4); + close(fd); +} + +int main(int argc, char **argv, char **envp) { + int uid = geteuid(); + int gid = getegid(); + unshare(CLONE_NEWUSER | CLONE_NEWNS); + mkdir ("dev", 0755); + touch ("dev/null"); + mount ("/dev/null", "dev/null", "", MS_BIND, NULL); + touch ("dev/zero"); + mount ("/dev/zero", "dev/zero", "", MS_BIND, NULL); + touch ("dev/random"); + mount ("/dev/random", "dev/random", "", MS_BIND, NULL); + touch ("dev/urandom"); + mount ("/dev/urandom", "dev/urandom", "", MS_BIND, NULL); + touch ("dev/ptmx"); + mount ("/dev/ptmx", "dev/ptmx", "", MS_BIND, NULL); + touch ("dev/tty"); + mount ("/dev/tty", "dev/tty", "", MS_BIND, NULL); + deny_setgroups(); + set_map(uid, "/proc/self/uid_map"); + set_map(gid, "/proc/self/gid_map"); + chroot ("."); + execve (argv[1], argv + 4 , envp); +} From f13aa2964a0d5ad73577124d1e5be4c4f6934254 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Mon, 27 Nov 2023 22:28:31 +0000 Subject: [PATCH 02/16] Make the operation of wrap.c conditional on need Don't create a user namespace if we are already root and don't chroot if cwd is already root --- sysa/wrap.c | 61 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index 048aa699..e9968d06 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -92,25 +92,44 @@ void deny_setgroups() { } int main(int argc, char **argv, char **envp) { - int uid = geteuid(); - int gid = getegid(); - unshare(CLONE_NEWUSER | CLONE_NEWNS); - mkdir ("dev", 0755); - touch ("dev/null"); - mount ("/dev/null", "dev/null", "", MS_BIND, NULL); - touch ("dev/zero"); - mount ("/dev/zero", "dev/zero", "", MS_BIND, NULL); - touch ("dev/random"); - mount ("/dev/random", "dev/random", "", MS_BIND, NULL); - touch ("dev/urandom"); - mount ("/dev/urandom", "dev/urandom", "", MS_BIND, NULL); - touch ("dev/ptmx"); - mount ("/dev/ptmx", "dev/ptmx", "", MS_BIND, NULL); - touch ("dev/tty"); - mount ("/dev/tty", "dev/tty", "", MS_BIND, NULL); - deny_setgroups(); - set_map(uid, "/proc/self/uid_map"); - set_map(gid, "/proc/self/gid_map"); - chroot ("."); - execve (argv[1], argv + 4 , envp); + char *cwd = get_current_dir_name(); + /* Do nothing if cwd is already root */ + if (strcmp(cwd, "/")) { + int uid = geteuid(); + int gid = getegid(); + /* Don't create a user and mount namespace if we are already root */ + if (uid != 0) { + require(unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0, "Failed to create user and mount namespaces"); + /* Prevent the use of setgroups and make gid_map writeable */ + deny_setgroups(); + /* Map the root user in the user namespace to our user id */ + set_map(uid, "/proc/self/uid_map"); + /* Map the root group in the user namespace to our group id */ + set_map(gid, "/proc/self/gid_map"); + } + mkdir ("dev", 0755); + touch ("dev/null"); + mount ("/dev/null", "dev/null", "", MS_BIND, NULL); + touch ("dev/zero"); + mount ("/dev/zero", "dev/zero", "", MS_BIND, NULL); + touch ("dev/random"); + mount ("/dev/random", "dev/random", "", MS_BIND, NULL); + touch ("dev/urandom"); + mount ("/dev/urandom", "dev/urandom", "", MS_BIND, NULL); + touch ("dev/ptmx"); + mount ("/dev/ptmx", "dev/ptmx", "", MS_BIND, NULL); + touch ("dev/tty"); + mount ("/dev/tty", "dev/tty", "", MS_BIND, NULL); + mkdir ("dev/shm", 0755); + mount ("tmpfs", "dev/shm", "tmpfs", 0, NULL); + mkdir ("proc", 0755); + mount ("proc", "proc", "proc", 0, NULL); + mkdir ("sys", 0755); + mount ("/sys", "sys", "", MS_BIND, NULL); + mkdir ("tmp", 0755); + mkdir ("tmpfs", "tmp", "tmpfs", 0, NULL); + chroot ("."); + } + free(cwd); + return execve (argv[1], argv + sizeof(char *) , envp); } From d0635afc99dfd56c0422a31e1dcf34593ecfc1d3 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Mon, 27 Nov 2023 23:41:16 +0000 Subject: [PATCH 03/16] allow compilation of wrap.c with gcc --- sysa/wrap.c | 46 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index e9968d06..0ca3094e 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -1,13 +1,22 @@ #define CLONE_NEWUSER 0x10000000 #define CLONE_NEWNS 0x00020000 #define MS_BIND 4096 +#define MNT_DETACH 0x00000002 +#define _GNU_SOURCE #include #include -#include +#include #include +#include +#include + +#ifdef __M2__ + +#include + int unshare(int flags) { asm ( "lea_ebx,[esp+DWORD] %4" @@ -60,18 +69,40 @@ int chroot(char *path) { ); } -int set_map(int parent_id, char *path) { - char *map_contents; - char *parent_id_str; +#else + +// From bootstrappable.c in M2libc + +void require(int bool, char* error) +{ + if(!bool) + { + fputs(error, stderr); + exit(EXIT_FAILURE); + } +} + +extern int unshare(int flags); + +extern int mount(const char *source, const char *target, + const char *filesystemtype, unsigned long mountflags, const void *data); + +#endif + +void set_map(int parent_id, char *path) { int fd = open(path, O_WRONLY, 0); require(fd != -1, "Cannot open map file"); - map_contents = calloc(37, sizeof(char)); + char *map_contents = calloc(38, sizeof(char)); +#ifdef __M2__ strcpy(map_contents, "0 "); - parent_id_str = int2str(parent_id, 10, 0); + char *parent_id_str = int2str(parent_id, 10, 0); strcat(map_contents, parent_id_str); strcat(map_contents, " 1"); +#else + snprintf(map_contents, 38, "0 %i 1", parent_id); +#endif write(fd, map_contents, strlen(map_contents)); write(STDOUT_FILENO, map_contents, strlen(map_contents)); free(map_contents); @@ -92,6 +123,7 @@ void deny_setgroups() { } int main(int argc, char **argv, char **envp) { + require(argc > 1, "Expected at least one argument: command"); char *cwd = get_current_dir_name(); /* Do nothing if cwd is already root */ if (strcmp(cwd, "/")) { @@ -127,7 +159,7 @@ int main(int argc, char **argv, char **envp) { mkdir ("sys", 0755); mount ("/sys", "sys", "", MS_BIND, NULL); mkdir ("tmp", 0755); - mkdir ("tmpfs", "tmp", "tmpfs", 0, NULL); + mount ("tmpfs", "tmp", "tmpfs", 0, NULL); chroot ("."); } free(cwd); From 1fb704334cbb9f737ad057ee49533277f85039cb Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 00:41:22 +0000 Subject: [PATCH 04/16] add x86_64 to wrap.c --- sysa/wrap.c | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/sysa/wrap.c b/sysa/wrap.c index 0ca3094e..00346e86 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -17,6 +17,8 @@ #include +#if __i386__ + int unshare(int flags) { asm ( "lea_ebx,[esp+DWORD] %4" @@ -69,6 +71,66 @@ int chroot(char *path) { ); } +#elif __x86_64__ + +int unshare(int flags) { + asm ( + "lea_rdi,[rsp+DWORD] %8" + "mov_rdi,[rdi]" + "mov_rax, %272" + "syscall" + ); +} + +int geteuid() { + asm ( + "mov_rax, %107" + "syscall" + ); +} + +int getegid() { + asm ( + "mov_rax, %108" + "syscall" + ); +} + +int mount( + char *source, char *target, char *filesystemtype, + unsigned mountflags, void *data +) { + asm ( + "DEFINE mov_r8,[rsp+DWORD] 4C8D8424" + "DEFINE mov_r10,[rsp+DWORD] 4C8B9424" + "lea_rdi,[rsp+DWORD] %40" + "mov_rdi,[rdi]" + "lea_rsi,[rsp+DWORD] %32" + "mov_rsi,[rsi]" + "lea_rdx,[rsp+DWORD] %24" + "mov_rdx,[rdx]" + "mov_r10,[rsp+DWORD] %16" + "mov_r8,[rsp+DWORD] %8" + "mov_rax, %165" + "syscall" + ); +} + +int chroot(char *path) { + asm ( + "lea_rdi,[rsp+DWORD] %8" + "mov_rdi,[rdi]" + "mov_rax, %161" + "syscall" + ); +} + +#else + +#error arch not supported + +#endif + #else // From bootstrappable.c in M2libc From c4e12161b76b16a84f6f8c225d30cbfefc7b2f82 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 01:50:53 +0000 Subject: [PATCH 05/16] reset environment in wrap.c --- sysa/wrap.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index 00346e86..677a174a 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -225,5 +225,19 @@ int main(int argc, char **argv, char **envp) { chroot ("."); } free(cwd); - return execve (argv[1], argv + sizeof(char *) , envp); + char **newenv = malloc(3 * sizeof(char *)); + char *ARCH = getenv("ARCH"); + newenv[0] = malloc(6 + strlen(ARCH)); + strcpy(newenv[0], "ARCH="); + strcpy(newenv[0] + 5, ARCH); + char *ARCH_DIR = getenv("ARCH_DIR"); + newenv[1] = malloc(10 + strlen(ARCH_DIR)); + strcpy(newenv[1], "ARCH_DIR="); + strcpy(newenv[1] + 9, ARCH_DIR); + newenv[2] = NULL; +#ifdef __M2__ + return execve (argv[1], argv + sizeof(char *) , newenv); +#else + return execve (argv[1], argv + 1, newenv); +#endif } From 53d1c416bef8775aea4808c4f00ead29e12e64b9 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 02:39:40 +0000 Subject: [PATCH 06/16] Only add variables to environment if they are already set --- sysa/wrap.c | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index 677a174a..50658a79 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -225,18 +225,35 @@ int main(int argc, char **argv, char **envp) { chroot ("."); } free(cwd); + + char **newenv = malloc(3 * sizeof(char *)); + int newenv_index = 0; + require(newenv != NULL, "Failed to allocate space for new environment."); + char *ARCH = getenv("ARCH"); - newenv[0] = malloc(6 + strlen(ARCH)); - strcpy(newenv[0], "ARCH="); - strcpy(newenv[0] + 5, ARCH); + if (ARCH != NULL) { + newenv[0] = malloc(6 + strlen(ARCH)); + require(newenv[0] != NULL, "Failed to allocate space for new environment."); + strcpy(newenv[0], "ARCH="); + strcpy(newenv[0] + 5, ARCH); + newenv_index += 1; + } + char *ARCH_DIR = getenv("ARCH_DIR"); - newenv[1] = malloc(10 + strlen(ARCH_DIR)); - strcpy(newenv[1], "ARCH_DIR="); - strcpy(newenv[1] + 9, ARCH_DIR); - newenv[2] = NULL; + if (ARCH_DIR != NULL) { + newenv[newenv_index] = malloc(10 + strlen(ARCH_DIR)); + require(newenv[newenv_index] != NULL, "Failed to allocate space for new environment."); + strcpy(newenv[newenv_index], "ARCH_DIR="); + strcpy(newenv[newenv_index] + 9, ARCH_DIR); + newenv_index += 1; + } + + newenv[newenv_index] = NULL; + + #ifdef __M2__ - return execve (argv[1], argv + sizeof(char *) , newenv); + return execve (argv[1], argv + sizeof(char *), newenv); #else return execve (argv[1], argv + 1, newenv); #endif From f4e139d1ac1c240d72fa478fa1abfa0499c159e3 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 03:11:51 +0000 Subject: [PATCH 07/16] remove envp argument for main --- sysa/wrap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index 50658a79..5943b496 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -184,7 +184,7 @@ void deny_setgroups() { close(fd); } -int main(int argc, char **argv, char **envp) { +int main(int argc, char **argv) { require(argc > 1, "Expected at least one argument: command"); char *cwd = get_current_dir_name(); /* Do nothing if cwd is already root */ From 7c320a5b75e3c3f13bb0c4cc8e3b7e30ff3e595f Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 03:12:29 +0000 Subject: [PATCH 08/16] fix hex code for mov_r8,[rsp+DWORD] --- sysa/wrap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index 5943b496..43dc0df0 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -101,7 +101,7 @@ int mount( unsigned mountflags, void *data ) { asm ( - "DEFINE mov_r8,[rsp+DWORD] 4C8D8424" + "DEFINE mov_r8,[rsp+DWORD] 4C8B8424" "DEFINE mov_r10,[rsp+DWORD] 4C8B9424" "lea_rdi,[rsp+DWORD] %40" "mov_rdi,[rdi]" From c487ab80c2052bcf40d465d7ea9425f7988b8b7e Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 03:12:50 +0000 Subject: [PATCH 09/16] mount proc and sys recursively --- sysa/wrap.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index 43dc0df0..5216d133 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -1,6 +1,7 @@ #define CLONE_NEWUSER 0x10000000 #define CLONE_NEWNS 0x00020000 #define MS_BIND 4096 +#define MS_REC 16384 #define MNT_DETACH 0x00000002 #define _GNU_SOURCE @@ -217,9 +218,9 @@ int main(int argc, char **argv) { mkdir ("dev/shm", 0755); mount ("tmpfs", "dev/shm", "tmpfs", 0, NULL); mkdir ("proc", 0755); - mount ("proc", "proc", "proc", 0, NULL); + mount ("/proc", "proc", "", MS_BIND | MS_REC, NULL); mkdir ("sys", 0755); - mount ("/sys", "sys", "", MS_BIND, NULL); + mount ("/sys", "sys", "", MS_BIND | MS_REC, NULL); mkdir ("tmp", 0755); mount ("tmpfs", "tmp", "tmpfs", 0, NULL); chroot ("."); From f3cf29ed11055067dbef3f92b4f349ac31e7d5c3 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 04:00:59 +0000 Subject: [PATCH 10/16] port to riscv{32,64} --- sysa/wrap.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/sysa/wrap.c b/sysa/wrap.c index 5216d133..0346699c 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -126,6 +126,100 @@ int chroot(char *path) { ); } +#elif __riscv && __riscv_xlen==32 + +int unshare(int flags) { + asm ( + "rd_a0 rs1_fp !-4 lw" + "rd_a7 !97 addi" + "ecall" + ); +} + +int geteuid() { + asm ( + "rd_a7 !175 addi" + "ecall" + ); +} + +int getegid() { + asm ( + "rd_a7 !177 addi" + "ecall" + ); +} + +int mount ( + char *source, char *target, char *filesystemtype, + unsigned mountflags, void *data +) { + asm ( + "rd_a0 rs1_fp !-4 lw" + "rd_a1 rs1_fp !-8 lw" + "rd_a2 rs1_fp !-12 lw" + "rd_a3 rs1_fp !-16 lw" + "rd_a4 rs1_fp !-20 lw" + "rd_a7 !40 addi" + "ecall" + ); +} + +int chroot(char *path) { + asm ( + "rd_a0 rs1_fp !-4 lw" + "rd_a7 !51 addi" + "ecall" + ); +} + +#elif __riscv && __riscv_xlen==64 + +int unshare(int flags) { + asm ( + "rd_a0 rs1_fp !-8 ld" + "rd_a7 !97 addi" + "ecall" + ); +} + +int geteuid() { + asm ( + "rd_a7 !175 addi" + "ecall" + ); +} + +int getegid() { + asm ( + "rd_a7 !177 addi" + "ecall" + ); +} + +int mount ( + char *source, char *target, char *filesystemtype, + unsigned mountflags, void *data +) { + asm ( + "rd_a0 rs1_fp !-8 ld" + "rd_a1 rs1_fp !-16 ld" + "rd_a2 rs1_fp !-24 ld" + "rd_a3 rs1_fp !-32 ld" + "rd_a4 rs1_fp !-40 ld" + "rd_a7 !40 addi" + "ecall" + ); +} + +int chroot(char *path) { + asm ( + "rd_a0 rs1_fp !-8 ld" + "rd_a7 !51 addi" + "ecall" + ); +} + #else #error arch not supported From b7c57cac8b62356c7af1bad498b01526b4deb017 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 04:24:18 +0000 Subject: [PATCH 11/16] improve error handling --- sysa/wrap.c | 138 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 92 insertions(+), 46 deletions(-) diff --git a/sysa/wrap.c b/sysa/wrap.c index 0346699c..ce6e88b3 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -228,17 +228,6 @@ int chroot(char *path) { #else -// From bootstrappable.c in M2libc - -void require(int bool, char* error) -{ - if(!bool) - { - fputs(error, stderr); - exit(EXIT_FAILURE); - } -} - extern int unshare(int flags); extern int mount(const char *source, const char *target, @@ -246,9 +235,57 @@ extern int mount(const char *source, const char *target, #endif +void touch(char *path) { + int fd = open(path, O_CREAT, 0777); + if (fd == -1) { + fputs("Failed to create file ", stderr); + fputs(path, stderr); + fputc('\n', stderr); + exit(EXIT_FAILURE); + } + if (close(fd) != 0) { + fputs("Failed to close file ", stderr); + fputs(path, stderr); + fputc('\n', stderr); + exit(EXIT_FAILURE); + } +} + +void mkmount( + char *source, char *target, char *filesystemtype, + unsigned mountflags, void *data, int type +) { + int r = 0; + if (type) { + r = mkdir(target, 0755); + } else { + touch(target); + } + if (r != 0 && r != -17) { + fputs("Failed to create mountpoint ", stderr); + fputs(target, stderr); + fputc('\n', stderr); + exit(EXIT_FAILURE); + } + + r = mount(source, target, filesystemtype, mountflags, data); + + if (r != 0) { + fputs("Failed to mount directory ", stderr); + fputs(target, stderr); + fputc('\n', stderr); + exit(EXIT_FAILURE); + } +} + void set_map(int parent_id, char *path) { int fd = open(path, O_WRONLY, 0); - require(fd != -1, "Cannot open map file"); + if (fd == -1) { + fputs("Failed to open map file ", stderr); + fputs(path, stderr); + fputc('\n', stderr); + exit(EXIT_FAILURE); + } char *map_contents = calloc(38, sizeof(char)); @@ -266,21 +303,21 @@ void set_map(int parent_id, char *path) { close(fd); } -void touch(char *path) { - int fd = open(path, O_CREAT, 0777); - require(fd != -1, "Cannot open file"); - close(fd); -} - void deny_setgroups() { int fd = open("/proc/self/setgroups", O_WRONLY, 0777); - require(fd != -1, "Failed to open /proc/self/setgroups"); + if(fd == -1) { + fputs("Failed to open /proc/self/setgroups\n", stderr); + exit(EXIT_FAILURE); + } write(fd, "deny", 4); close(fd); } int main(int argc, char **argv) { - require(argc > 1, "Expected at least one argument: command"); + if(argc <= 1) { + fputs("Expected at least one argument: command\n", stderr); + exit(EXIT_FAILURE); + } char *cwd = get_current_dir_name(); /* Do nothing if cwd is already root */ if (strcmp(cwd, "/")) { @@ -288,7 +325,10 @@ int main(int argc, char **argv) { int gid = getegid(); /* Don't create a user and mount namespace if we are already root */ if (uid != 0) { - require(unshare(CLONE_NEWUSER | CLONE_NEWNS) == 0, "Failed to create user and mount namespaces"); + if (unshare(CLONE_NEWUSER | CLONE_NEWNS) != 0) { + fputs("Failed to create user and mount namespaces\n", stderr); + exit(EXIT_FAILURE); + } /* Prevent the use of setgroups and make gid_map writeable */ deny_setgroups(); /* Map the root user in the user namespace to our user id */ @@ -296,40 +336,43 @@ int main(int argc, char **argv) { /* Map the root group in the user namespace to our group id */ set_map(gid, "/proc/self/gid_map"); } - mkdir ("dev", 0755); - touch ("dev/null"); - mount ("/dev/null", "dev/null", "", MS_BIND, NULL); - touch ("dev/zero"); - mount ("/dev/zero", "dev/zero", "", MS_BIND, NULL); - touch ("dev/random"); - mount ("/dev/random", "dev/random", "", MS_BIND, NULL); - touch ("dev/urandom"); - mount ("/dev/urandom", "dev/urandom", "", MS_BIND, NULL); - touch ("dev/ptmx"); - mount ("/dev/ptmx", "dev/ptmx", "", MS_BIND, NULL); - touch ("dev/tty"); - mount ("/dev/tty", "dev/tty", "", MS_BIND, NULL); - mkdir ("dev/shm", 0755); - mount ("tmpfs", "dev/shm", "tmpfs", 0, NULL); - mkdir ("proc", 0755); - mount ("/proc", "proc", "", MS_BIND | MS_REC, NULL); - mkdir ("sys", 0755); - mount ("/sys", "sys", "", MS_BIND | MS_REC, NULL); - mkdir ("tmp", 0755); - mount ("tmpfs", "tmp", "tmpfs", 0, NULL); - chroot ("."); + int r = mkdir("dev", 0755); + if (r != 0 && r != -17) { + fputs("Failed to create dev folder\n", stderr); + exit(EXIT_FAILURE); + } + mkmount ("/dev/null", "dev/null", "", MS_BIND, NULL, 0); + mkmount ("/dev/zero", "dev/zero", "", MS_BIND, NULL, 0); + mkmount ("/dev/random", "dev/random", "", MS_BIND, NULL, 0); + mkmount ("/dev/urandom", "dev/urandom", "", MS_BIND, NULL, 0); + mkmount ("/dev/ptmx", "dev/ptmx", "", MS_BIND, NULL, 0); + mkmount ("/dev/tty", "dev/tty", "", MS_BIND, NULL, 0); + mkmount ("tmpfs", "dev/shm", "tmpfs", 0, NULL, 1); + mkmount ("/proc", "proc", "", MS_BIND | MS_REC, NULL, 1); + mkmount ("/sys", "sys", "", MS_BIND | MS_REC, NULL, 1); + mkmount ("tmpfs", "tmp", "tmpfs", 0, NULL, 1); + if (chroot (".") != 0) { + fputs("Failed to chroot into .\n", stderr); + exit(EXIT_FAILURE); + } } free(cwd); char **newenv = malloc(3 * sizeof(char *)); int newenv_index = 0; - require(newenv != NULL, "Failed to allocate space for new environment."); + if (newenv == NULL) { + fputs("Failed to allocate space for new environment\n", stderr); + exit(EXIT_FAILURE); + } char *ARCH = getenv("ARCH"); if (ARCH != NULL) { newenv[0] = malloc(6 + strlen(ARCH)); - require(newenv[0] != NULL, "Failed to allocate space for new environment."); + if (newenv[0] == NULL) { + fputs("Failed to allocate space for new environment\n", stderr); + exit(EXIT_FAILURE); + } strcpy(newenv[0], "ARCH="); strcpy(newenv[0] + 5, ARCH); newenv_index += 1; @@ -338,7 +381,10 @@ int main(int argc, char **argv) { char *ARCH_DIR = getenv("ARCH_DIR"); if (ARCH_DIR != NULL) { newenv[newenv_index] = malloc(10 + strlen(ARCH_DIR)); - require(newenv[newenv_index] != NULL, "Failed to allocate space for new environment."); + if (newenv[newenv_index] == NULL) { + fputs("Failed to allocate space for new environment\n", stderr); + exit(EXIT_FAILURE); + } strcpy(newenv[newenv_index], "ARCH_DIR="); strcpy(newenv[newenv_index] + 9, ARCH_DIR); newenv_index += 1; From f7adeba3d8ce01f28178436a9f91060a3e775c8e Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 04:48:00 +0000 Subject: [PATCH 12/16] Allow the user to use wrap as a build step --- rootfs.py | 20 +++++++++++++++++++- sysa.py | 12 ++++++++---- sysa/run2.sh | 9 +++++++++ 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/rootfs.py b/rootfs.py index f4a6d226..6dc1fe52 100755 --- a/rootfs.py +++ b/rootfs.py @@ -31,8 +31,9 @@ def create_configuration_file(args): config_path = os.path.join('sysa', 'bootstrap.cfg') with open(config_path, "w", encoding="utf_8") as config: config.write(f"FORCE_TIMESTAMPS={args.force_timestamps}\n") - config.write(f"CHROOT={args.chroot or args.bwrap}\n") + config.write(f"CHROOT={args.chroot or args.wrap or args.bwrap}\n") config.write(f"CHROOT_ONLY_SYSA={args.bwrap}\n") + config.write(f"CHROOT_WRAP={args.wrap}\n") config.write(f"UPDATE_CHECKSUMS={args.update_checksums}\n") config.write(f"JOBS={args.cores}\n") config.write(f"INTERNAL_CI={args.internal_ci}\n") @@ -59,6 +60,8 @@ def main(): action="store_true") parser.add_argument("-bw", "--bwrap", help="Run inside a bwrap sandbox", action="store_true") + parser.add_argument("-w", "--wrap", help="Use builtin unprivileged wrapper", + action="store_true") parser.add_argument("-p", "--preserve", help="Do not remove temporary dir", action="store_true") parser.add_argument("-t", "--tmpdir", help="Temporary directory", @@ -113,6 +116,8 @@ def check_types(): count += 1 if args.bwrap: count += 1 + if args.wrap: + count += 1 if args.bare_metal: count += 1 return count @@ -131,6 +136,9 @@ def check_types(): if args.bwrap and args.tmpfs: raise ValueError("tmpfs cannot be used with bwrap.") + if args.wrap and args.tmpfs: + raise ValueError("tmpfs cannot be used with wrap.") + # Cores validation if int(args.cores) < 1: raise ValueError("Must use one or more cores.") @@ -223,6 +231,16 @@ def bootstrap(args, system_a, system_c, tmpdir): '--bind', '/sys', '/sys', '--tmpfs', '/tmp', '/init') + + elif args.wrap: + system_c.prepare(create_disk_image=False) + system_a.prepare(create_initramfs=False, wrap=True) + + arch = stage0_arch_map.get(args.arch, args.arch) + init = os.path.join('bootstrap-seeds', 'POSIX', arch, 'kaem-optional-seed') + + os.chdir(system_a.tmp_dir) + run(init) elif args.bare_metal: if args.kernel: diff --git a/sysa.py b/sysa.py index d474109a..5cb9214d 100755 --- a/sysa.py +++ b/sysa.py @@ -37,7 +37,7 @@ def __init__(self, tmpdir, arch, external_sources, self.tmp_dir = tmpdir.add_sys("sysa") - def prepare(self, create_initramfs, kernel_bootstrap=False): + def prepare(self, create_initramfs, kernel_bootstrap=False, wrap=False): """ Prepare directory structure for System A. We create an empty tmp directory, unpack stage0-posix. @@ -50,7 +50,7 @@ def prepare(self, create_initramfs, kernel_bootstrap=False): shutil.copy2(os.path.join(self.sys_dir, 'base-preseeded.kaem'), os.path.join(self.tmp_dir, 'kaem.x86')) else: - self.stage0_posix() + self.stage0_posix(wrap) self.sysa() @@ -93,7 +93,7 @@ def sysc(self, create_initramfs): shutil.copytree(self.sysc_dir, os.path.join(self.tmp_dir, 'sysc'), ignore=ignore) - def stage0_posix(self): + def stage0_posix(self, wrap): """Copy in all of the stage0-posix""" stage0_posix_base_dir = os.path.join(self.sys_dir, 'stage0-posix', 'src') copy_tree(stage0_posix_base_dir, self.tmp_dir) @@ -104,7 +104,11 @@ def stage0_posix(self): shutil.copy2(kaem_optional_seed, os.path.join(self.tmp_dir, 'init')) # stage0-posix hook to continue running live-bootstrap - shutil.copy2(os.path.join(self.sys_dir, 'after.kaem'), + if wrap: + after_kaem_name = "after_wrap.kaem" + else: + after_kaem_name = "after.kaem" + shutil.copy2(os.path.join(self.sys_dir, after_kaem_name), os.path.join(self.tmp_dir, 'after.kaem')) def add_fiwix_files(self, file_list_path, dirpath): diff --git a/sysa/run2.sh b/sysa/run2.sh index 6d21eca7..dbe4c9d5 100755 --- a/sysa/run2.sh +++ b/sysa/run2.sh @@ -114,6 +114,15 @@ else SYSC=/sysc_image sys_transfer "${SYSC}" /sysc gzip patch if [ "${CHROOT_ONLY_SYSA}" != True ]; then + if [ "${CHROOT_WRAP}" = True ]; then + # bind mount dev, proc and sys into new root + mkdir -p "${SYSC}/dev" + mount --no-mtab --rbind /dev "${SYSC}/dev" + mkdir -p "${SYSC}/proc" + mount --no-mtab --rbind /proc "${SYSC}/proc" + mkdir -p "${SYSC}/sys" + mount --no-mtab --rbind /sys "${SYSC}/sys" + fi exec chroot "${SYSC}" /init fi fi From 3ef06654b71a08c1837da3da3db4e3c394cae27d Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 04:57:24 +0000 Subject: [PATCH 13/16] add set -ex to after_wrap.kaem --- sysa/after_wrap.kaem | 2 ++ 1 file changed, 2 insertions(+) diff --git a/sysa/after_wrap.kaem b/sysa/after_wrap.kaem index 4bfca595..15f1081c 100644 --- a/sysa/after_wrap.kaem +++ b/sysa/after_wrap.kaem @@ -1,5 +1,7 @@ #!/bin/sh +set -ex + PATH=./${ARCH_DIR}/bin ./${ARCH_DIR}/bin/M2-Mesoplanet --architecture ${ARCH} \ From fd1aa8f0425dea7c87a28c7bc90c2e763a74499f Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 05:01:22 +0000 Subject: [PATCH 14/16] add licenses --- sysa/after_wrap.kaem | 3 +++ sysa/wrap.c | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/sysa/after_wrap.kaem b/sysa/after_wrap.kaem index 15f1081c..361447d0 100644 --- a/sysa/after_wrap.kaem +++ b/sysa/after_wrap.kaem @@ -1,5 +1,8 @@ #!/bin/sh +# SPDX-FileCopyrightText: 2023 Max Hearnden +# SPDX-License-Identifier: GPL-3.0-or-later + set -ex PATH=./${ARCH_DIR}/bin diff --git a/sysa/wrap.c b/sysa/wrap.c index ce6e88b3..c16dde17 100644 --- a/sysa/wrap.c +++ b/sysa/wrap.c @@ -1,3 +1,7 @@ +/* SPDX-FileCopyrightText: 2023 Max Hearnden */ +/* SPDX-License-Identifier: GPL-3.0-or-later */ + + #define CLONE_NEWUSER 0x10000000 #define CLONE_NEWNS 0x00020000 #define MS_BIND 4096 From 31f4ef41fd04fda7069e8155da82f1149f0ab03b Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 05:09:18 +0000 Subject: [PATCH 15/16] remove trailing whitespace from rootfs.py --- rootfs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootfs.py b/rootfs.py index 6dc1fe52..0015375f 100755 --- a/rootfs.py +++ b/rootfs.py @@ -231,7 +231,7 @@ def bootstrap(args, system_a, system_c, tmpdir): '--bind', '/sys', '/sys', '--tmpfs', '/tmp', '/init') - + elif args.wrap: system_c.prepare(create_disk_image=False) system_a.prepare(create_initramfs=False, wrap=True) From 255164c66496bc646c3a1e9262deb2df05aa82a8 Mon Sep 17 00:00:00 2001 From: MaxHearnden Date: Tue, 28 Nov 2023 21:39:23 +0000 Subject: [PATCH 16/16] use cwd attribute rather than os.chdir() --- rootfs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/rootfs.py b/rootfs.py index 0015375f..cda68cf7 100755 --- a/rootfs.py +++ b/rootfs.py @@ -239,8 +239,7 @@ def bootstrap(args, system_a, system_c, tmpdir): arch = stage0_arch_map.get(args.arch, args.arch) init = os.path.join('bootstrap-seeds', 'POSIX', arch, 'kaem-optional-seed') - os.chdir(system_a.tmp_dir) - run(init) + run(init, cwd = system_a.tmp_dir) elif args.bare_metal: if args.kernel: