From 08afc3a98b7385ea2439dec1aac0f41c4c14c408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karel=20Ko=C4=8D=C3=AD?= Date: Sun, 2 Dec 2018 21:09:06 +0100 Subject: Update to working version Still way off from something usable by someone else but it already works for me. --- Makefile.am | 2 +- README.md | 51 ++++++++++++++++++++++++++++++ child.c | 60 +++++++++++++++++++++++++++++++++++ child.h | 37 ++++++++++++++++++++++ conf.h | 25 +++++++++++++++ main.c | 103 +++++++++++++++++++++++++++++++----------------------------- sigpipe.c | 46 +++++++++++++++++++++++++++ sigpipe.h | 34 ++++++++++++++++++++ utils.c | 55 ++++++++++++++++++++++++++++++++ utils.h | 34 +++++++++++++++++--- 10 files changed, 392 insertions(+), 55 deletions(-) create mode 100644 README.md create mode 100644 child.c create mode 100644 child.h create mode 100644 conf.h create mode 100644 sigpipe.c create mode 100644 sigpipe.h diff --git a/Makefile.am b/Makefile.am index a451a6d..711f06a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,2 +1,2 @@ bin_PROGRAMS = uroot -uroot_SOURCES = main.c utils.c +uroot_SOURCES = main.c utils.c child.c sigpipe.c diff --git a/README.md b/README.md new file mode 100644 index 0000000..b456289 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +uroot (User's root) +=================== +Tool that uses Linux namespaces and resource separation to provide root like +functionality even under unprivileged user on Linux systems. This is more commonly +known as unprivileged containers. Difference between this project and full fledge +containerization solution is that this tools tries to provide less separation to +allow more versatile uses. See example usages if you are not sure what this can +do. There is also section with limitations stating what you can't do with this +tool. + +In general this tools allows you to imaginary become root. This has a lot of use +cases outside of just creating full containers. You can use it to control some bad +behaving program without fully separating it from host system. + +System setup +------------ +TODO (describe shadow requirements) + +Limitations of this tools +------------------------- +This tool is not perfect as well as technology it uses is not perfect. There can +be bugs and there are for sure unimplemented features. This section provides you +with information about some known problems that we are unable to solve because of +limitations of used technology. Please check this list before you report problem +or even before you use tool it self. + +### Block devices are no go +Unfortunately current implementation of namespaces, primarily mount points +unshare, does not support usage of block devices subsystem. That is kernel +subsystem handling access to storage devices. Most of kernel file system drivers +are implemented on top of block devices and because of that non of those file +systems can be used. This means that you can modify (mount) only already mounted +file systems or system file systems such as tmpfs or procfs. Allowing user access +to `/dev` device is not enough to fix this issue. This also means that you are not +able to use FUSE file systems. + +Example usages +-------------- +On top of making you look cool that you are able to get root on system you should +not (those hacking skills) this tool also have some real live uses. Some of them +can be clear cut but some usages might not be immediately clear. That is the main +reason why this section exists. It also should give you hints to common traps. + +### chroot +TODO + +### Single killable process +TODO + +### Network isolation +TODO diff --git a/child.c b/child.c new file mode 100644 index 0000000..d087064 --- /dev/null +++ b/child.c @@ -0,0 +1,60 @@ +/* uroot - User's root + * child.c Source file for child with unshared resources + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#define _GNU_SOURCE +#include "child.h" +#include +#include +#include +#include +#include +#include +#include "utils.h" + +int child_main(void *_args) { + struct child_args *args = _args; + sigpipe_wait(args->sigpipe); + + // Change some mount points to private + mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL); + assert_perror(errno); + /* + mount("none", "/proc", NULL, MS_REC | MS_PRIVATE, NULL); + assert_perror(errno); + mount("none", "/sys", NULL, MS_REC | MS_PRIVATE, NULL); + assert_perror(errno); + */ + // Mount new proc filesystem for this namespace + mount("proc", "/proc", "proc", MS_NOSUID | MS_NODEV | MS_NOEXEC, NULL); + assert_perror(errno); + //mount("binfmt_misc", "/proc/sys/fs/binfmt_misc", "binfmt_misc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL); + //assert_perror(errno); + + if (args->argc <= 1) { + const char *shell = get_shell(); + execl(shell, shell, NULL); + } else { + char *new_argv[args->argc + 1]; + memcpy(new_argv, args->argv + 1, args->argc * sizeof *new_argv); + new_argv[args->argc] = NULL; + execvp(args->argv[1], new_argv); + assert_perror(errno); + } + return 1; +} diff --git a/child.h b/child.h new file mode 100644 index 0000000..18909e4 --- /dev/null +++ b/child.h @@ -0,0 +1,37 @@ +/* uroot - User's root + * child.h Header file for child with unshared resources + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _UROOT_CHILD_H_ +#define _UROOT_CHILD_H_ + +#include +#include "sigpipe.h" + +// Arguments passed to child_main function +struct child_args { + pid_t ppid; // Paret pid + int argc; + char **argv; + sigpipe_t sigpipe; +}; + +// Function used as a main for child process +int child_main(void *_args); + +#endif diff --git a/conf.h b/conf.h new file mode 100644 index 0000000..eb18439 --- /dev/null +++ b/conf.h @@ -0,0 +1,25 @@ +/* uroot - User's root + * conf.h Header file with various configuration values + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +// Default shell used if SHELL variable is not set +#define DEFAULT_SHELL "/bin/sh" + +// Size of stack used by child process (at least untill exec is called) +#define STACK_SIZE ( 1024 * 1024 ) diff --git a/main.c b/main.c index a20f6ff..7957be3 100644 --- a/main.c +++ b/main.c @@ -1,58 +1,63 @@ +/* uroot - User's root + * main.c Source file with main function + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ #define _GNU_SOURCE #include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include -#include #include +#include +#include +#include +#include +#include "child.h" #include "utils.h" +#include "sigpipe.h" +#include "conf.h" -void sigint_handler(int sig) { } int main(int argc, char **argv) { - pid_t ppid = getpid(); - - if (!fork()) { - system(aprintf("newuidmap %d 0 %d 1 1 65537 65536", ppid, getuid())); - system(aprintf("newgidmap %d 0 %d 1 1 65537 65536", ppid, getgid())); - kill(ppid, SIGINT); - return 0; - } - - unshare(CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWPID); - - signal(SIGINT, sigint_handler); - pause(); - errno = 0; // Just clear error from pause() - - pid_t chpid = fork(); - if (chpid) { - int stat; - waitpid(chpid, &stat, 0); - return stat; - } - - // mount /sys and /proc - mount("none", "/", NULL, MS_REC | MS_PRIVATE, NULL); - assert_perror(errno); - mount("none", "/proc", NULL, MS_REC | MS_PRIVATE, NULL); - assert_perror(errno); - mount("none", "/sys", NULL, MS_REC | MS_PRIVATE, NULL); - assert_perror(errno); - mount("proc", "/proc", "proc", MS_NOSUID|MS_NODEV|MS_NOEXEC, NULL); - assert_perror(errno); - - // mount proc to root - // rbind mount dev and sys to root - - // TODO verify that all upper directories have +rx rights - // TODO chroot - execv("/bin/sh", NULL); + // TODO arguments parsing + + struct child_args chargs = (struct child_args) { + .ppid = getpid(), + .argc = argc, + .argv = argv, + .sigpipe = sigpipe_new() + }; + + uint8_t stack[STACK_SIZE]; + pid_t chpid = clone(child_main, stack + STACK_SIZE, + SIGCHLD | CLONE_NEWNS | CLONE_NEWUSER | CLONE_NEWPID, + &chargs); + +#define FAIL(MSG) do { fputs(MSG, stderr); kill(chpid, SIGKILL); return 1; } while (false); + if (new_map_id("uid", chpid, getuid())) + FAIL("Mapping of uid failed!\n"); + if (new_map_id("gid", chpid, getgid())) + FAIL("Mapping of gid failed!\n"); + + sigpipe_signal(chargs.sigpipe); + + int stat; + waitpid(chpid, &stat, 0); + if (WIFEXITED(stat)) + return WEXITSTATUS(stat); + else + return WIFSIGNALED(stat) || WCOREDUMP(stat); } diff --git a/sigpipe.c b/sigpipe.c new file mode 100644 index 0000000..d653cbe --- /dev/null +++ b/sigpipe.c @@ -0,0 +1,46 @@ +/* uroot - User's root + * sigpipe.c Source file for signaling pipe implementation + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#define _GNU_SOURCE +#include "sigpipe.h" +#include +#include +#include +#include + +sigpipe_t sigpipe_new() { + sigpipe_t sp = malloc(2 * sizeof(int)); + if (!pipe(sp)) + assert_perror(errno); + return sp; +} + +void sigpipe_wait(sigpipe_t sp) { + close(sp[1]); + char ch; + while (read(sp[0], &ch, 1) > 0); + assert_perror(errno); + free(sp); +} + +void sigpipe_signal(sigpipe_t sp) { + close(sp[0]); + close(sp[1]); + free(sp); +} diff --git a/sigpipe.h b/sigpipe.h new file mode 100644 index 0000000..9152b69 --- /dev/null +++ b/sigpipe.h @@ -0,0 +1,34 @@ +/* uroot - User's root + * sigpipe.h Header file for signaling pipe implementation + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _UROOT_SIGPIPE_H_ +#define _UROOT_SIGPIPE_H_ + + +typedef int* sigpipe_t; + +// Create new sigpipe +sigpipe_t sigpipe_new(); +// Wait until other end calls sigpipe_signal. +// It returns immediately in case it already happened. +void sigpipe_wait(sigpipe_t); +// Notify waiting end that it can continue execution. +void sigpipe_signal(sigpipe_t); + +#endif diff --git a/utils.c b/utils.c index 04806dd..e4f771d 100644 --- a/utils.c +++ b/utils.c @@ -1,4 +1,31 @@ +/* uroot - User's root + * utils.c Source file with various utility functions + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include #include "utils.h" +#include "conf.h" // Compute the size needed (including \0) to format given message size_t printf_len(const char *msg, ...) { @@ -17,3 +44,31 @@ char *printf_into(char *dst, const char *msg, ...) { va_end(args); return dst; } + +const char *get_shell() { + const char *ret; + ret = secure_getenv("SHELL"); + if (!ret) + ret = DEFAULT_SHELL; + return ret; +} + +int new_map_id(const char *idtp, pid_t pid, int id) { + // TODO check /etc/sub* for current user subids and map them + pid_t chld = fork(); + if (!chld) { + char *tool = aprintf("new%smap", idtp); + char *const args[] = { + tool, aprintf("%d", pid), + "0", aprintf("%d", id), "1", + "1", "65537", "65536", + NULL + }; + execvp(tool, args); + abort(); + } + int stat; + waitpid(chld, &stat, 0); + return stat; +} + diff --git a/utils.h b/utils.h index 9e5984d..b9c943f 100644 --- a/utils.h +++ b/utils.h @@ -1,10 +1,28 @@ -#ifndef _UTILS_H_ -#define _UTILS_H_ +/* uroot - User's root + * utils.h Header file with various utility functions + * + * Copyright (C) 2018 Karel Kočí + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _UROOT_UTILS_H_ +#define _UROOT_UTILS_H_ #define _GNU_SOURCE -#include -#include #include +#include // Compute the size needed (including \0) to format given message size_t printf_len(const char *msg, ...) __attribute__((format(printf, 1, 2))); @@ -14,4 +32,10 @@ char *printf_into(char *dst, const char *msg, ...) __attribute__((format(printf, // uses the arguments multiple times, so beware of side effects. #define aprintf(...) printf_into(alloca(printf_len(__VA_ARGS__)), __VA_ARGS__) -#endif /* _UTILS_H_ */ +// returns path to shell interpreter +const char *get_shell(); + +// call newuidmap and newgidmap for process of given pid +int new_map_id(const char *idtp, pid_t pid, int id); + +#endif -- cgit v1.2.3