diff --git a/Base/usr/share/man/man8/chroot.md b/Base/usr/share/man/man8/chroot.md index ce8e06d7b2..522f2c47f9 100644 --- a/Base/usr/share/man/man8/chroot.md +++ b/Base/usr/share/man/man8/chroot.md @@ -24,6 +24,12 @@ Mount options can be given in the same format as for [`mount`(8)](mount.md). / ``` +```sh +# chroot -u 200:200 /var/chroot +$ id +uid=200(nona) gid=200(n/a) +``` + ## See also * [`chroot`(2)](../man2/chroot.md) diff --git a/Userland/chroot.cpp b/Userland/chroot.cpp index a8e4c13a64..36e4daf691 100644 --- a/Userland/chroot.cpp +++ b/Userland/chroot.cpp @@ -27,19 +27,40 @@ #include #include #include +#include +#include #include int main(int argc, char** argv) { + int flags = -1; + uid_t chroot_user = 0; + gid_t chroot_group = 0; const char* path = nullptr; const char* program = "/bin/Shell"; - int flags = -1; + const char* userspec = "0:0"; Core::ArgsParser args_parser; args_parser.add_positional_argument(path, "New root directory", "path"); args_parser.add_positional_argument(program, "Program to run", "program", Core::ArgsParser::Required::No); - Core::ArgsParser::Option option { + Core::ArgsParser::Option userspec_option { + true, + "The uid:gid to use", + "userspec", + 'u', + "userpec", + [&userspec](const char* s) { + Vector parts = StringView(s).split_view(':', true); + if (parts.size() != 2) + return false; + userspec = s; + return true; + } + }; + args_parser.add_option(move(userspec_option)); + + Core::ArgsParser::Option mount_options { true, "Mount options", "options", @@ -69,7 +90,7 @@ int main(int argc, char** argv) return true; } }; - args_parser.add_option(move(option)); + args_parser.add_option(move(mount_options)); args_parser.parse(argc, argv); if (chroot_with_mount_flags(path, flags) < 0) { @@ -82,6 +103,22 @@ int main(int argc, char** argv) return 1; } + // Failed parsing will silently fail open (uid=0; gid=0); + // 0:0 is also the default when no --userspec argument is provided. + auto parts = String(userspec).split(':', true); + chroot_user = (uid_t)strtol(parts[0].characters(), nullptr, 10); + chroot_group = (uid_t)strtol(parts[1].characters(), nullptr, 10); + + if (setresgid(chroot_group, chroot_group, chroot_group)) { + perror("setgid"); + return 1; + } + + if (setresuid(chroot_user, chroot_user, chroot_user)) { + perror("setuid"); + return 1; + } + execl(program, program, nullptr); perror("execl"); return 1;