From b45242c50fdb2fa0ed0d04f9dd6242ff1c009e13 Mon Sep 17 00:00:00 2001 From: Norman Feske Date: Wed, 24 Oct 2012 16:27:26 +0200 Subject: [PATCH] Add chroot support to core Since the recent move of the process creation into core, the original chroot trampoline mechanism implemented in 'os/src/app/chroot' does not work anymore. A process could simply escape the chroot environment by spawning a new process via core's PD service. Therefore, this patch moves the chroot support into core. So the chroot policy becomes mandatory part of the process creation. For each process created by core, core checks for 'root' argument of the PD session. If a path is present, core takes the precautions needed to execute the new process in the specified chroot environment. This conceptual change implies minor changes with respect to the Genode API and the configuration of the init process. The API changes are the enhancement of the 'Genode::Child' and 'Genode::Process' constructors to take the root path as argument. Init supports the specification of a chroot per process by specifying the new 'root' attribute to the '' node of the process. In line with these changes, the 'Loader::Session::start' function has been enhanced with the additional (optional) root argument. --- base-linux/src/base/process/process.cc | 4 +- .../src/core/include/core_linux_syscalls.h | 29 +++ .../src/core/include/pd_session_component.h | 4 +- base-linux/src/core/pd_session_component.cc | 189 +++++++++++++++- base/include/base/child.h | 5 + base/include/base/process.h | 6 +- base/include/pd_session/connection.h | 9 +- base/src/base/child/child.cc | 2 +- base/src/base/process/process.cc | 4 +- demo/include/launchpad/launchpad.h | 2 +- os/doc/init.txt | 23 ++ os/include/init/child.h | 37 +++- os/include/init/child_policy.h | 57 ++++- os/include/loader_session/client.h | 4 +- os/include/loader_session/loader_session.h | 6 +- os/run/chroot.run | 33 +-- os/run/chroot_loader.run | 13 +- os/src/app/chroot/main.cc | 207 ------------------ os/src/app/chroot/target.mk | 4 - os/src/server/loader/child.h | 21 +- os/src/server/loader/main.cc | 9 +- os/src/test/chroot_loader/main.cc | 28 +-- 22 files changed, 401 insertions(+), 295 deletions(-) delete mode 100644 os/src/app/chroot/main.cc delete mode 100644 os/src/app/chroot/target.mk diff --git a/base-linux/src/base/process/process.cc b/base-linux/src/base/process/process.cc index f3064c166..73c611d5c 100644 --- a/base-linux/src/base/process/process.cc +++ b/base-linux/src/base/process/process.cc @@ -58,9 +58,9 @@ Process::Process(Dataspace_capability elf_data_ds_cap, Rm_session_capability rm_session_cap, Parent_capability parent_cap, char const *name, - char *const argv[]) + char const *root) : - _pd(name), + _pd(name, root), _cpu_session_client(Cpu_session_capability()), _rm_session_client(Rm_session_capability()) { diff --git a/base-linux/src/core/include/core_linux_syscalls.h b/base-linux/src/core/include/core_linux_syscalls.h index 659ffd434..33047db86 100644 --- a/base-linux/src/core/include/core_linux_syscalls.h +++ b/base-linux/src/core/include/core_linux_syscalls.h @@ -90,6 +90,35 @@ inline int lx_create_process(int (*entry)(void *), void *stack, void *arg) } +/********************* + ** Chroot handling ** + *********************/ + +inline int lx_chroot(const char *path) +{ + return lx_syscall(SYS_chroot, path); +} + + +inline int lx_getcwd(char *dst, size_t dst_len) +{ + return lx_syscall(SYS_getcwd, dst, dst_len); +} + + +inline int lx_bindmount(char const *source, char const *target) +{ + enum { MS_BIND = 4096 }; + return lx_syscall(SYS_mount, source, target, 0, MS_BIND, 0); +} + + +inline int lx_umount(char const *target) +{ + return lx_syscall(SYS_umount, target); +} + + /******************************************** ** Communication over Unix-domain sockets ** ********************************************/ diff --git a/base-linux/src/core/include/pd_session_component.h b/base-linux/src/core/include/pd_session_component.h index ac81753c1..51929547b 100644 --- a/base-linux/src/core/include/pd_session_component.h +++ b/base-linux/src/core/include/pd_session_component.h @@ -27,10 +27,12 @@ namespace Genode { { private: - enum { LABEL_MAX_LEN = 1024 }; + enum { LABEL_MAX_LEN = 1024 }; + enum { ROOT_PATH_MAX_LEN = 512 }; unsigned long _pid; char _label[LABEL_MAX_LEN]; + char _root[ROOT_PATH_MAX_LEN]; Parent_capability _parent; Rpc_entrypoint *_ds_ep; diff --git a/base-linux/src/core/pd_session_component.cc b/base-linux/src/core/pd_session_component.cc index 7eb619f3a..d6700e1f4 100644 --- a/base-linux/src/core/pd_session_component.cc +++ b/base-linux/src/core/pd_session_component.cc @@ -26,6 +26,166 @@ using namespace Genode; +/*********************************** + ** Utilities for chroot handling ** + ***********************************/ + +enum { MAX_PATH_LEN = 256 }; + + +/** + * Return true if specified path is an existing directory + */ +static bool is_directory(char const *path) +{ + struct stat64 s; + if (lx_stat(path, &s) != 0) + return false; + + if (!(s.st_mode & S_IFDIR)) + return false; + + return true; +} + + +static bool is_path_delimiter(char c) { return c == '/'; } + + +static bool has_trailing_path_delimiter(char const *path) +{ + char last_char = 0; + for (; *path; path++) + last_char = *path; + + return is_path_delimiter(last_char); +} + + +/** + * Return number of path elements of given path + */ +static Genode::size_t num_path_elements(char const *path) +{ + Genode::size_t count = 0; + + /* + * If path starts with non-slash, the first characters belongs to a path + * element. + */ + if (*path && !is_path_delimiter(*path)) + count = 1; + + /* count slashes */ + for (; *path; path++) + if (is_path_delimiter(*path)) + count++; + + return count; +} + + +static bool leading_path_elements(char const *path, unsigned num, + char *dst, Genode::size_t dst_len) +{ + /* counter of path delimiters */ + unsigned count = 0; + unsigned i = 0; + + if (is_path_delimiter(path[0])) + num++; + + for (; path[i] && (count < num) && (i < dst_len); i++) + { + if (is_path_delimiter(path[i])) + count++; + + if (count == num) + break; + + dst[i] = path[i]; + } + + if (i + 1 < dst_len) { + dst[i] = 0; + return true; + } + + /* string is cut, append null termination anyway */ + dst[dst_len - 1] = 0; + return false; +} + + +static void mirror_path_to_chroot(char const *chroot_path, char const *path) +{ + char target_path[MAX_PATH_LEN]; + Genode::snprintf(target_path, sizeof(target_path), "%s%s", + chroot_path, path); + + /* + * Create directory hierarchy pointing to the target path except for the + * last element. The last element will be bind-mounted to refer to the + * original 'path'. + */ + for (unsigned i = 1; i <= num_path_elements(target_path); i++) + { + char buf[MAX_PATH_LEN]; + leading_path_elements(target_path, i, buf, sizeof(buf)); + + /* skip existing directories */ + if (is_directory(buf)) + continue; + + /* create new directory */ + lx_mkdir(buf, 0777); + } + + lx_umount(target_path); + + int ret = 0; + if ((ret = lx_bindmount(path, target_path))) + PERR("bind mount failed (errno=%d)", ret); +} + + +/** + * Setup content of chroot environment as prerequisite to 'execve' new + * processes within the environment. I.e., the current working directory + * containing the ROM modules must be mounted at the same location within the + * chroot environment. + */ +static bool setup_chroot_environment(char const *chroot_path) +{ + using namespace Genode; + + static char cwd_path[MAX_PATH_LEN]; + + lx_getcwd(cwd_path, sizeof(cwd_path)); + + /* + * Validate chroot path + */ + if (!is_directory(chroot_path)) { + PERR("chroot path does not point to valid directory"); + return false; + } + + if (has_trailing_path_delimiter(chroot_path)) { + PERR("chroot path has trailing slash"); + return false; + } + + /* + * Hardlink directories needed for running Genode within the chroot + * environment. + */ + mirror_path_to_chroot(chroot_path, cwd_path); + + return true; +} + + /*************** ** Utilities ** ***************/ @@ -35,7 +195,8 @@ using namespace Genode; */ struct Execve_args { - const char *filename; + char const *filename; + char const *root; char *const *argv; char *const *envp; int parent_sd; @@ -49,6 +210,26 @@ static int _exec_child(Execve_args *arg) { lx_dup2(arg->parent_sd, PARENT_SOCKET_HANDLE); + /* change to chroot environment */ + if (arg->root && arg->root[0]) { + + PDBG("arg->root='%s'", arg->root); + + if (setup_chroot_environment(arg->root) == false) { + PERR("Could not setup chroot environment"); + return -1; + } + + PLOG("changing root of %s (PID %d) to %s", + arg->filename, lx_getpid(), arg->root); + + int ret = lx_chroot(arg->root); + if (ret < 0) { + PERR("Syscall chroot failed (errno %d)", ret); + return -1; + } + } + return lx_execve(arg->filename, arg->argv, arg->envp); } @@ -85,6 +266,7 @@ Pd_session_component::Pd_session_component(Rpc_entrypoint *ep, const char *args) { Arg_string::find_arg(args, "label").string(_label, sizeof(_label), ""); + Arg_string::find_arg(args, "root").string(_root, sizeof(_root), ""); } @@ -95,8 +277,7 @@ Pd_session_component::~Pd_session_component() } -int Pd_session_component::bind_thread(Thread_capability) { return -1; -} +int Pd_session_component::bind_thread(Thread_capability) { return -1; } int Pd_session_component::assign_parent(Parent_capability parent) @@ -161,7 +342,7 @@ void Pd_session_component::start(Capability binary) * Argument frame as passed to 'clone'. Because, we can only pass a single * pointer, all arguments are embedded within the 'execve_args' struct. */ - Execve_args arg = { filename.buf, argv_buf, env, _parent.dst().socket }; + Execve_args arg = { filename.buf, _root, argv_buf, env, _parent.dst().socket }; _pid = lx_create_process((int (*)(void *))_exec_child, stack + STACK_SIZE - sizeof(umword_t), &arg); diff --git a/base/include/base/child.h b/base/include/base/child.h index 94531e1c3..3b5ada522 100644 --- a/base/include/base/child.h +++ b/base/include/base/child.h @@ -93,6 +93,11 @@ namespace Genode { * transfers. */ virtual Ram_session *ref_ram_session() { return env()->ram_session(); } + + /** + * Return root path of the child's PD session (only used on Linux) + */ + virtual char const *root() const { return ""; } }; diff --git a/base/include/base/process.h b/base/include/base/process.h index 39e5ef1fe..baa2a9de1 100644 --- a/base/include/base/process.h +++ b/base/include/base/process.h @@ -58,7 +58,7 @@ namespace Genode { * \param parent parent of the new protection domain * \param name name of protection domain (can be used * in debugging) - * \param argv not used + * \param root optional chroot path (only on Linux) * * The dataspace 'elf_data_ds' can be read-only. * @@ -70,8 +70,8 @@ namespace Genode { Cpu_session_capability cpu_session, Rm_session_capability rm_session, Parent_capability parent, - const char *name, - char *const argv[]); + char const *name, + char const *root = ""); /** * Destructor diff --git a/base/include/pd_session/connection.h b/base/include/pd_session/connection.h index 6edb1567c..c93e01081 100644 --- a/base/include/pd_session/connection.h +++ b/base/include/pd_session/connection.h @@ -25,10 +25,15 @@ namespace Genode { * Constructor * * \param label session label + * \param root chroot path (only on Linux) */ - Pd_connection(const char *label = "") + Pd_connection(char const *label = "", char const *root = "") : - Connection(session("ram_quota=4K, label=\"%s\"", label)), + Connection(session("ram_quota=4K, label=\"%s\"%s%s%s", + label, + (root && root[0]) ? ", root=\"" : "", + (root && root[0]) ? root : "", + (root && root[0]) ? "\"" : "")), Pd_session_client(cap()) { } }; diff --git a/base/src/base/child/child.cc b/base/src/base/child/child.cc index 00b209dfe..85611f0ab 100644 --- a/base/src/base/child/child.cc +++ b/base/src/base/child/child.cc @@ -431,7 +431,7 @@ Child::Child(Dataspace_capability elf_ds, _parent_cap(_entrypoint->manage(this)), _policy(policy), _server(ram), - _process(elf_ds, ram, cpu, rm, _parent_cap, policy->name(), 0) + _process(elf_ds, ram, cpu, rm, _parent_cap, policy->name(), policy->root()) { } diff --git a/base/src/base/process/process.cc b/base/src/base/process/process.cc index 342e3b56d..c007108ac 100644 --- a/base/src/base/process/process.cc +++ b/base/src/base/process/process.cc @@ -175,9 +175,9 @@ Process::Process(Dataspace_capability elf_ds_cap, Rm_session_capability rm_session_cap, Parent_capability parent_cap, char const *name, - char *const argv[]) + char const *pd_args) : - _pd(name), + _pd(name, pd_args), _cpu_session_client(cpu_session_cap), _rm_session_client(rm_session_cap) { diff --git a/demo/include/launchpad/launchpad.h b/demo/include/launchpad/launchpad.h index e57ee80da..9f542a190 100644 --- a/demo/include/launchpad/launchpad.h +++ b/demo/include/launchpad/launchpad.h @@ -42,7 +42,7 @@ class Launchpad_child_policy : public Init::Traditional_child_policy : Init::Traditional_child_policy(name, server, parent_services, child_services, config_ds, - binary_ds, 0, 0, parent_entrypoint) + binary_ds, 0, 0, 0, parent_entrypoint) { } Genode::Service *resolve_session_request(const char *service_name, diff --git a/os/doc/init.txt b/os/doc/init.txt index 82e32b8ee..88f85ae31 100644 --- a/os/doc/init.txt +++ b/os/doc/init.txt @@ -262,6 +262,29 @@ as LOG output. To enable the verbose mode, assign the value "yes" to the 'verbose' attribute of the '' node. +Executing children in chroot environments on Linux +================================================== + +On the Linux base platform, each process started by init can be assigned to +a chroot environment by specifying the new root location as 'root' attribute +to the corresponding '' node. Root environments can be nested. The +root path of a nested init instance will be appended to the root path of +the outer instance. + +When using the chroot mechanism, core will mirror the current working +directory within the chroot environment via the a bind mount operation. This +step is needed to enable execve to obtain the ELF binary of the new process. + +In order to use the chroot mechanism when starting Genode's core as a non-root +user process, the core executable must be equipped with the 'CAP_SYS_ADMIN' and +'CAP_SYS_CHROOT' capabilities. 'CAP_SYS_ADMIN' is needed for bind mounting. +'CAP_SYS_CHROOT' is needed to perform the 'chroot' syscall: + +! sudo setcap cap_sys_admin,cap_sys_chroot=ep core + +For an example of using chroot, please refer to the 'os/run/chroot.run' script. + + Using the configuration concept ############################### diff --git a/os/include/init/child.h b/os/include/init/child.h index e9f465736..552d9e388 100644 --- a/os/include/init/child.h +++ b/os/include/init/child.h @@ -298,6 +298,31 @@ namespace Init { } } _name; + /** + * Path of the child's chroot environment (on Linux) + */ + struct Root + { + /* + * XXX dimension ROOT_PATH_LEN depending on the platform + */ + enum { ROOT_PATH_LEN = 256 }; + char path[ROOT_PATH_LEN]; + + /** + * Constructor + */ + Root(Genode::Xml_node start_node) + { + path[0] = 0; + + try { + start_node.attribute("root").value(path, sizeof(path)); } + catch (Genode::Xml_node::Nonexistent_attribute) { } + } + + } _root; + /** * Resources assigned to the child */ @@ -369,6 +394,7 @@ namespace Init { Init::Child_policy_provide_rom_file _config_policy; Init::Child_policy_provide_rom_file _binary_policy; Init::Child_policy_redirect_rom_file _configfile_policy; + Init::Child_policy_prepend_chroot_path _chroot_policy; public: @@ -385,6 +411,7 @@ namespace Init { _default_route_node(default_route_node), _name_registry(name_registry), _name(start_node, name_registry), + _root(start_node), _resources(start_node, _name.unique, prio_levels_log2), _entrypoint(cap_session, ENTRYPOINT_STACK_SIZE, _name.unique, false), _binary_rom(_name.file, _name.unique), @@ -398,7 +425,8 @@ namespace Init { _priority_policy(_resources.prio_levels_log2, _resources.priority), _config_policy("config", _config.dataspace(), &_entrypoint), _binary_policy("binary", _binary_rom.dataspace(), &_entrypoint), - _configfile_policy("config", _config.filename()) + _configfile_policy("config", _config.filename()), + _chroot_policy(_root.path) { using namespace Genode; @@ -545,9 +573,10 @@ namespace Init { void filter_session_args(const char *service, char *args, Genode::size_t args_len) { - _labeling_policy.filter_session_args(service, args, args_len); - _priority_policy.filter_session_args(service, args, args_len); + _labeling_policy. filter_session_args(service, args, args_len); + _priority_policy. filter_session_args(service, args, args_len); _configfile_policy.filter_session_args(service, args, args_len); + _chroot_policy. filter_session_args(service, args, args_len); } bool announce_service(const char *service_name, @@ -569,6 +598,8 @@ namespace Init { rs->announce(root); return true; } + + char const *root() const { return _root.path; } }; } diff --git a/os/include/init/child_policy.h b/os/include/init/child_policy.h index f644efdb8..505167aaa 100644 --- a/os/include/init/child_policy.h +++ b/os/include/init/child_policy.h @@ -63,6 +63,53 @@ namespace Init { }; + /** + * Policy for prepending the chroot path of the child + * + * This policy is effective only on the Linux base platform. + * + * By applying this policy, the chroot path of the child gets supplied + * to PD session requests. + */ + class Child_policy_prepend_chroot_path + { + private: + + char const *_root_prefix; + + public: + + Child_policy_prepend_chroot_path(const char *root_prefix) + : _root_prefix(root_prefix) { } + + /** + * Filter arguments of session request + * + * This function prepends the '_root' to the 'root' session + * argument of PD sessions initiated through the child (not the + * child's PD session). + */ + void filter_session_args(const char *session, char *args, + Genode::size_t args_len) + { + using namespace Genode; + + if (strcmp(session, "PD") != 0) + return; + + char path_buf[Parent::Session_args::MAX_SIZE]; + Arg_string::find_arg(args, "root").string(path_buf, sizeof(path_buf), ""); + + char value_buf[Parent::Session_args::MAX_SIZE]; + Genode::snprintf(value_buf, sizeof(value_buf), + "\"%s%s\"", + _root_prefix, path_buf); + + Arg_string::set_arg(args, args_len, "root", value_buf); + } + }; + + class Child_policy_handle_cpu_priorities { /* priority parameters */ @@ -237,6 +284,8 @@ namespace Init { enum { NAME_LEN = 64 }; char _name[NAME_LEN]; + char const *_root; + Genode::Server *_server; Genode::Service_registry *_parent_services; Genode::Service_registry *_child_services; @@ -260,8 +309,10 @@ namespace Init { Genode::Dataspace_capability binary_ds, long prio_levels_log2, long priority, + char const *root, Genode::Rpc_entrypoint *parent_entrypoint) : + _root(root), _server(server), _parent_services(parent_services), _child_services(child_services), @@ -270,8 +321,10 @@ namespace Init { _labeling_policy(_name), _priority_policy(prio_levels_log2, priority), _config_policy("config", config_ds, _parent_entrypoint), - _binary_policy("binary", binary_ds, _parent_entrypoint) { - Genode::strncpy(_name, name, sizeof(_name)); } + _binary_policy("binary", binary_ds, _parent_entrypoint) + { + Genode::strncpy(_name, name, sizeof(_name)); + } const char *name() const { return _name; } diff --git a/os/include/loader_session/client.h b/os/include/loader_session/client.h index 131321680..519c20dab 100644 --- a/os/include/loader_session/client.h +++ b/os/include/loader_session/client.h @@ -41,8 +41,8 @@ namespace Loader { void view_ready_sigh(Signal_context_capability sigh) { call(sigh); } - void start(Name const &binary, Name const &label = "") { - call(binary, label); } + void start(Name const &binary, Name const &label = "", Path const &root = "") { + call(binary, label, root); } Nitpicker::View_capability view() { return call(); } diff --git a/os/include/loader_session/loader_session.h b/os/include/loader_session/loader_session.h index 72b8c12a0..d1537879c 100644 --- a/os/include/loader_session/loader_session.h +++ b/os/include/loader_session/loader_session.h @@ -54,6 +54,7 @@ namespace Loader { }; typedef Genode::Rpc_in_buffer<64> Name; + typedef Genode::Rpc_in_buffer<128> Path; static const char *service_name() { return "Loader"; } @@ -121,7 +122,8 @@ namespace Loader { * \throw Rom_module_does_not_exist if the specified binary could * not obtained as ROM module */ - virtual void start(Name const &binary, Name const &label = "") = 0; + virtual void start(Name const &binary, Name const &label = "", + Path const &root = "") = 0; /** * Return first nitpicker view created by the loaded subsystem @@ -152,7 +154,7 @@ namespace Loader { GENODE_RPC(Rpc_view_ready_sigh, void, view_ready_sigh, Signal_context_capability); GENODE_RPC_THROW(Rpc_start, void, start, GENODE_TYPE_LIST(Rom_module_does_not_exist), - Name const &, Name const &); + Name const &, Name const &, Path const &); GENODE_RPC_THROW(Rpc_view, Nitpicker::View_capability, view, GENODE_TYPE_LIST(View_does_not_exist)); GENODE_RPC(Rpc_view_geometry, View_geometry, view_geometry); diff --git a/os/run/chroot.run b/os/run/chroot.run index a3b25ca77..cbb8fc5db 100644 --- a/os/run/chroot.run +++ b/os/run/chroot.run @@ -10,7 +10,7 @@ if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 } # Build # -build { core init app/chroot drivers/timer/linux test/timer } +build { core init drivers/timer/linux test/timer } if {[catch { exec which setcap }]} { puts stderr "Error: setcap not available, please install the libcap2-bin package" @@ -42,27 +42,8 @@ set config { - + - - - - - - - - - - - - - - - - - - - } @@ -91,16 +72,16 @@ install_config $config # run directory into the chroot environment. If the directory entries # were symlinks, those would point to nowhere within the chroot. # -foreach binary { core init chroot timer test-timer } { +foreach binary { core init timer test-timer } { exec cp -H bin/$binary [run_dir] } # -# Grant chroot permission to 'chroot' tool +# Grant chroot permission to core # # CAP_SYS_ADMIN is needed for bind mounting genode runtime directories # CAP_SYS_CHROOT is needed to perform the chroot syscall # -exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/chroot +exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/core # # Setup chroot environment @@ -111,8 +92,8 @@ cleanup_chroot exec mkdir -p [chroot_path] exec mkdir -p [chroot_path]/lib -# bind mount '/lib' as need libc within the chroot environment -exec sudo mount --bind /lib [chroot_path]/lib +# bind mount '/lib' as we need ldso and libc within the chroot environment +exec sudo mount --bind /lib [chroot_path]/lib # # Execute test case diff --git a/os/run/chroot_loader.run b/os/run/chroot_loader.run index d4536f4c4..9c120ebd3 100644 --- a/os/run/chroot_loader.run +++ b/os/run/chroot_loader.run @@ -10,7 +10,7 @@ if {![have_spec linux]} { puts "Run script requires Linux"; exit 0 } # Build # -build { core init app/chroot drivers/timer/linux test/timer +build { core init drivers/timer/linux test/timer server/loader test/chroot_loader } if {[catch { exec which setcap }]} { @@ -62,12 +62,9 @@ proc chroot_path { id } { return "/tmp/chroot-test-$id" } proc chroot_cwd_path { id } { return "[chroot_path $id][pwd]/[run_dir]" } -proc chroot_genode_tmp_path { id } { return "[chroot_path $id]/tmp/genode-[exec id -u]" } - proc cleanup_chroot { } { foreach id { 1 2 } { catch { exec sudo umount -l [chroot_cwd_path $id] } - catch { exec sudo umount -l [chroot_genode_tmp_path $id] } catch { exec sudo umount -l [chroot_path $id]/lib } catch { exec sudo umount -l [chroot_path $id]/lib64 } catch { exec rm -rf [chroot_path $id] } @@ -88,16 +85,16 @@ install_config $config # run directory into the chroot environment. If the directory entries # were symlinks, those would point to nowhere within the chroot. # -foreach binary { core init chroot timer loader test-chroot_loader test-timer} { +foreach binary { core init timer loader test-chroot_loader test-timer} { exec cp -H bin/$binary [run_dir] } # -# Grant chroot permission to 'chroot' tool +# Grant chroot permission to 'core' # # CAP_SYS_ADMIN is needed for bind mounting genode runtime directories # CAP_SYS_CHROOT is needed to perform the chroot syscall # -exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/chroot +exec sudo setcap cap_sys_admin,cap_sys_chroot=ep [run_dir]/core # # Setup chroot environment @@ -129,7 +126,7 @@ if {[regexp -all -- {--- timer test ---} $output] != 6} { exit 2 } -if {![regexp -- {chroot-1 -> test-timer] wait 2/10} $output]} { +if {![regexp -- {init-1 -> test-timer] wait 2/10} $output]} { puts stderr "Long-running timer test has made too little progress" exit 3 } diff --git a/os/src/app/chroot/main.cc b/os/src/app/chroot/main.cc deleted file mode 100644 index e8ef01e95..000000000 --- a/os/src/app/chroot/main.cc +++ /dev/null @@ -1,207 +0,0 @@ -/* - * \brief Utility for using the Linux chroot mechanism with Genode - * \author Norman Feske - * \date 2012-04-18 - */ - -/* Genode includes */ -#include -#include - -/* Linux includes */ -#include -#include -#include -#include -#include - - -enum { MAX_PATH_LEN = 256 }; - -static bool verbose = false; - - -/** - * Return true if specified path is an existing directory - */ -static bool is_directory(char const *path) -{ - struct stat s; - if (stat(path, &s) != 0) - return false; - - if (!(s.st_mode & S_IFDIR)) - return false; - - return true; -} - - -static bool is_path_delimiter(char c) { return c == '/'; } - - -static bool has_trailing_path_delimiter(char const *path) -{ - char last_char = 0; - for (; *path; path++) - last_char = *path; - - return is_path_delimiter(last_char); -} - - -/** - * Return number of path elements of given path - */ -static size_t num_path_elements(char const *path) -{ - size_t count = 0; - - /* - * If path starts with non-slash, the first characters belongs to a path - * element. - */ - if (*path && !is_path_delimiter(*path)) - count = 1; - - /* count slashes */ - for (; *path; path++) - if (is_path_delimiter(*path)) - count++; - - return count; -} - - -static bool leading_path_elements(char const *path, unsigned num, - char *dst, size_t dst_len) -{ - /* counter of path delimiters */ - unsigned count = 0; - unsigned i = 0; - - if (is_path_delimiter(path[0])) - num++; - - for (; path[i] && (count < num) && (i < dst_len); i++) - { - if (is_path_delimiter(path[i])) - count++; - - if (count == num) - break; - - dst[i] = path[i]; - } - - if (i + 1 < dst_len) { - dst[i] = 0; - return true; - } - - /* string is cut, append null termination anyway */ - dst[dst_len - 1] = 0; - return false; -} - - -static void mirror_path_to_chroot(char const *chroot_path, char const *path) -{ - char target_path[MAX_PATH_LEN]; - Genode::snprintf(target_path, sizeof(target_path), "%s%s", - chroot_path, path); - - /* - * Create directory hierarchy pointing to the target path except for the - * last element. The last element will be bind-mounted to refer to the - * original 'path'. - */ - for (unsigned i = 1; i <= num_path_elements(target_path); i++) - { - char buf[MAX_PATH_LEN]; - leading_path_elements(target_path, i, buf, sizeof(buf)); - - /* skip existing directories */ - if (is_directory(buf)) - continue; - - /* create new directory */ - mkdir(buf, 0777); - } - - umount(target_path); - - if (verbose) { - PINF("bind mount from: %s", path); - PINF(" to: %s", target_path); - } - - if (mount(path, target_path, 0, MS_BIND, 0)) - PERR("bind mount failed (errno=%d: %s)", errno, strerror(errno)); -} - - -int main(int, char **argv) -{ - using namespace Genode; - - static char chroot_path[MAX_PATH_LEN]; - static char cwd_path[MAX_PATH_LEN]; - - /* - * Read configuration - */ - try { - Xml_node config = Genode::config()->xml_node(); - - config.sub_node("root").attribute("path") - .value(chroot_path, sizeof(chroot_path)); - - verbose = config.attribute("verbose").has_value("yes"); - - } catch (...) { - PERR("invalid config"); - return 1; - } - - getcwd(cwd_path, sizeof(cwd_path)); - - /* - * Print diagnostic information - */ - if (verbose) { - PINF("work directory: %s", cwd_path); - PINF("chroot path: %s", chroot_path); - } - - /* - * Validate chroot path - */ - if (!is_directory(chroot_path)) { - PERR("chroot path does not point to valid directory"); - return 2; - } - - if (has_trailing_path_delimiter(chroot_path)) { - PERR("chroot path has trailing slash"); - return 3; - } - - /* - * Hardlink directories needed for running Genode within the chroot - * environment. - */ - mirror_path_to_chroot(chroot_path, cwd_path); - - printf("changing root to %s ...\n", chroot_path); - - if (chroot(chroot_path)) { - PERR("chroot failed (errno=%d: %s)", errno, strerror(errno)); - return 4; - } - - execve("init", argv, environ); - - PERR("execve failed with errno=%d", errno); - return 0; -} diff --git a/os/src/app/chroot/target.mk b/os/src/app/chroot/target.mk deleted file mode 100644 index 5a49c45b4..000000000 --- a/os/src/app/chroot/target.mk +++ /dev/null @@ -1,4 +0,0 @@ -TARGET = chroot -REQUIRES = linux -SRC_CC = main.cc -LIBS = cxx env server lx_hybrid diff --git a/os/src/server/loader/child.h b/os/src/server/loader/child.h index a3c8db88f..382738bcf 100644 --- a/os/src/server/loader/child.h +++ b/os/src/server/loader/child.h @@ -36,6 +36,11 @@ namespace Loader { Label(char const *l) { strncpy(string, l, sizeof(string)); } } _label; + struct Path { + char string[Session::Path::MAX_SIZE]; + Path(char const *l) { strncpy(string, l, sizeof(string)); } + } _root; + Rpc_entrypoint &_ep; struct Resources @@ -70,8 +75,9 @@ namespace Loader { Rom_session_client _binary_rom_session; - Init::Child_policy_provide_rom_file _binary_policy; - Init::Child_policy_enforce_labeling _labeling_policy; + Init::Child_policy_provide_rom_file _binary_policy; + Init::Child_policy_enforce_labeling _labeling_policy; + Init::Child_policy_prepend_chroot_path _chroot_policy; int _max_width, _max_height; @@ -91,8 +97,9 @@ namespace Loader { public: - Child(const char *binary_name, - const char *label, + Child(char const *binary_name, + char const *label, + char const *root, Rpc_entrypoint &ep, Ram_session_client &ram_session_client, size_t ram_quota, @@ -102,6 +109,7 @@ namespace Loader { int max_width, int max_height) : _label(label), + _root(root), _ep(ep), _resources(_label.string, ram_session_client, ram_quota), _parent_services(parent_services), @@ -110,6 +118,7 @@ namespace Loader { _binary_rom_session(_rom_session(binary_name)), _binary_policy("binary", _binary_rom_session.dataspace(), &_ep), _labeling_policy(_label.string), + _chroot_policy(_root.string), _max_width(max_width), _max_height(max_height), _child(_binary_rom_session.dataspace(), _resources.ram.cap(), _resources.cpu.cap(), @@ -127,10 +136,12 @@ namespace Loader { ****************************/ const char *name() const { return _label.string; } + char const *root() const { return _root.string; } void filter_session_args(char const *service, char *args, size_t args_len) { - _labeling_policy.filter_session_args(0, args, args_len); + _labeling_policy.filter_session_args(service, args, args_len); + _chroot_policy. filter_session_args(service, args, args_len); if (!strcmp(service, "Nitpicker")) { diff --git a/os/src/server/loader/main.cc b/os/src/server/loader/main.cc index 2533d5ca8..6496565e7 100644 --- a/os/src/server/loader/main.cc +++ b/os/src/server/loader/main.cc @@ -244,7 +244,7 @@ namespace Loader { _nitpicker_service.view_ready_sigh = sigh; } - void start(Name const &binary_name, Name const &label) + void start(Name const &binary_name, Name const &label, Path const &root) { if (_child) { PWRN("cannot start subsystem twice"); @@ -257,9 +257,10 @@ namespace Loader { try { _child = new (&_md_alloc) - Child(binary_name.string(), label.string(), _ep, - _ram_session_client, ram_quota, _parent_services, - _rom_service, _nitpicker_service, _width, _height); + Child(binary_name.string(), label.string(), + root.string(), _ep, _ram_session_client, + ram_quota, _parent_services, _rom_service, + _nitpicker_service, _width, _height); } catch (Genode::Parent::Service_denied) { throw Rom_module_does_not_exist(); } diff --git a/os/src/test/chroot_loader/main.cc b/os/src/test/chroot_loader/main.cc index 8b2fce76c..c604e19ba 100644 --- a/os/src/test/chroot_loader/main.cc +++ b/os/src/test/chroot_loader/main.cc @@ -48,14 +48,11 @@ static char const *chroot_path_of_dynamic_test() **********/ /** - * Return format string used as template for the subsystem configuration. - * - * Note the format-string argument used for inserting the chroot path. + * Return subsystem configuration. */ -static char const *config_template() +static char const *subsystem_config() { return "\n" - " \n" " \n" " \n" " \n" @@ -90,7 +87,7 @@ class Chroot_subsystem /** * Import data as ROM module into the subsystem-specific ROM service */ - void _import_rom_module(char const *name, void *ptr, Genode::size_t size) + void _import_rom_module(char const *name, void const *ptr, Genode::size_t size) { using namespace Genode; @@ -113,20 +110,17 @@ class Chroot_subsystem using namespace Genode; /* - * Generate Genode configuration of the new subsystem and import - * it into the subsystem's loader session as a ROM module named - * "config". + * Import subsystem's configuration into the subsystem's loader + * session as a ROM module named "config". */ - char buf[strlen(chroot_path) + strlen(config_template()) + 1]; - snprintf(buf, sizeof(buf), config_template(), chroot_path); - - _import_rom_module("config", buf, strlen(buf) + 1); + _import_rom_module("config", subsystem_config(), + strlen(subsystem_config()) + 1); /* * Name of the Genode binary is start as the root of the new * subsystem. */ - char const *chroot_binary_name = "chroot"; + char const *binary_name = "init"; /* * Generate unique label name using a counter @@ -136,10 +130,12 @@ class Chroot_subsystem * for validating the test in the run script. */ static int cnt = 0; - snprintf(_label, sizeof(_label), "%s-%d", chroot_binary_name, ++cnt); + snprintf(_label, sizeof(_label), "%s-%d", binary_name, ++cnt); /* start execution of new subsystem */ - _loader.start(chroot_binary_name, Loader::Session::Name(_label)); + _loader.start(binary_name, + Loader::Session::Name(_label), + Loader::Session::Path(chroot_path)); } };