mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 17:27:35 +00:00
Kernel: Add a basic chroot() syscall :^)
The chroot() syscall now allows the superuser to isolate a process into a specific subtree of the filesystem. This is not strictly permanent, as it is also possible for a superuser to break *out* of a chroot, but it is a useful mechanism for isolating unprivileged processes. The VFS now uses the current process's root_directory() as the root for path resolution purposes. The root directory is stored as an uncached Custody in the Process object.
This commit is contained in:
parent
944fbf507a
commit
ddd0b19281
7 changed files with 63 additions and 8 deletions
|
@ -52,7 +52,7 @@ KResult VFS::mount(NonnullRefPtr<FS>&& file_system, Custody& mount_point)
|
||||||
KResult VFS::mount(NonnullRefPtr<FS>&& file_system, StringView path)
|
KResult VFS::mount(NonnullRefPtr<FS>&& file_system, StringView path)
|
||||||
{
|
{
|
||||||
LOCKER(m_lock);
|
LOCKER(m_lock);
|
||||||
auto result = resolve_path(path, root_custody());
|
auto result = resolve_path(path, current->process().root_directory());
|
||||||
if (result.is_error()) {
|
if (result.is_error()) {
|
||||||
dbg() << "VFS: mount can't resolve mount point '" << path << "'";
|
dbg() << "VFS: mount can't resolve mount point '" << path << "'";
|
||||||
return result.error();
|
return result.error();
|
||||||
|
@ -667,11 +667,13 @@ KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& ba
|
||||||
auto parts = path.split_view('/', true);
|
auto parts = path.split_view('/', true);
|
||||||
InodeIdentifier crumb_id;
|
InodeIdentifier crumb_id;
|
||||||
|
|
||||||
|
auto& current_root = current->process().root_directory();
|
||||||
|
|
||||||
NonnullRefPtrVector<Custody, 32> custody_chain;
|
NonnullRefPtrVector<Custody, 32> custody_chain;
|
||||||
|
|
||||||
if (path[0] == '/') {
|
if (path[0] == '/') {
|
||||||
custody_chain.append(root_custody());
|
custody_chain.append(current_root);
|
||||||
crumb_id = root_inode_id();
|
crumb_id = current_root.inode().identifier();
|
||||||
} else {
|
} else {
|
||||||
for (auto* custody = &base; custody; custody = custody->parent()) {
|
for (auto* custody = &base; custody; custody = custody->parent()) {
|
||||||
// FIXME: Prepending here is not efficient! Fix this.
|
// FIXME: Prepending here is not efficient! Fix this.
|
||||||
|
@ -684,7 +686,7 @@ KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& ba
|
||||||
*parent_custody = custody_chain.last();
|
*parent_custody = custody_chain.last();
|
||||||
|
|
||||||
for (int i = 0; i < parts.size(); ++i) {
|
for (int i = 0; i < parts.size(); ++i) {
|
||||||
bool inode_was_root_at_head_of_loop = crumb_id.is_root_inode();
|
bool inode_was_root_at_head_of_loop = current_root.inode().identifier() == crumb_id;
|
||||||
auto crumb_inode = get_inode(crumb_id);
|
auto crumb_inode = get_inode(crumb_id);
|
||||||
if (!crumb_inode)
|
if (!crumb_inode)
|
||||||
return KResult(-EIO);
|
return KResult(-EIO);
|
||||||
|
@ -715,7 +717,7 @@ KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& ba
|
||||||
}
|
}
|
||||||
if (auto mount = find_mount_for_host(crumb_id))
|
if (auto mount = find_mount_for_host(crumb_id))
|
||||||
crumb_id = mount->guest();
|
crumb_id = mount->guest();
|
||||||
if (inode_was_root_at_head_of_loop && crumb_id.is_root_inode() && !is_vfs_root(crumb_id) && part == "..") {
|
if (inode_was_root_at_head_of_loop && crumb_id.is_root_inode() && crumb_id != current_root.inode().identifier() && part == "..") {
|
||||||
auto mount = find_mount_for_guest(crumb_id);
|
auto mount = find_mount_for_guest(crumb_id);
|
||||||
auto dir_inode = get_inode(mount->host());
|
auto dir_inode = get_inode(mount->host());
|
||||||
ASSERT(dir_inode);
|
ASSERT(dir_inode);
|
||||||
|
@ -724,7 +726,7 @@ KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& ba
|
||||||
crumb_inode = get_inode(crumb_id);
|
crumb_inode = get_inode(crumb_id);
|
||||||
ASSERT(crumb_inode);
|
ASSERT(crumb_inode);
|
||||||
|
|
||||||
custody_chain.append(Custody::get_or_create(&custody_chain.last(), part, *crumb_inode));
|
custody_chain.append(Custody::create(&custody_chain.last(), part, *crumb_inode));
|
||||||
|
|
||||||
metadata = crumb_inode->metadata();
|
metadata = crumb_inode->metadata();
|
||||||
if (metadata.is_directory()) {
|
if (metadata.is_directory()) {
|
||||||
|
|
|
@ -565,6 +565,7 @@ pid_t Process::sys$fork(RegisterDump& regs)
|
||||||
{
|
{
|
||||||
Thread* child_first_thread = nullptr;
|
Thread* child_first_thread = nullptr;
|
||||||
auto* child = new Process(child_first_thread, m_name, m_uid, m_gid, m_pid, m_ring, m_cwd, m_executable, m_tty, this);
|
auto* child = new Process(child_first_thread, m_name, m_uid, m_gid, m_pid, m_ring, m_cwd, m_executable, m_tty, this);
|
||||||
|
child->m_root_directory = m_root_directory;
|
||||||
|
|
||||||
#ifdef FORK_DEBUG
|
#ifdef FORK_DEBUG
|
||||||
dbgprintf("fork: child=%p\n", child);
|
dbgprintf("fork: child=%p\n", child);
|
||||||
|
@ -966,15 +967,21 @@ Process* Process::create_user_process(Thread*& first_thread, const String& path,
|
||||||
arguments.append(parts.last());
|
arguments.append(parts.last());
|
||||||
}
|
}
|
||||||
RefPtr<Custody> cwd;
|
RefPtr<Custody> cwd;
|
||||||
|
RefPtr<Custody> root;
|
||||||
{
|
{
|
||||||
InterruptDisabler disabler;
|
InterruptDisabler disabler;
|
||||||
if (auto* parent = Process::from_pid(parent_pid))
|
if (auto* parent = Process::from_pid(parent_pid)) {
|
||||||
cwd = parent->m_cwd;
|
cwd = parent->m_cwd;
|
||||||
|
root = parent->m_root_directory;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cwd)
|
if (!cwd)
|
||||||
cwd = VFS::the().root_custody();
|
cwd = VFS::the().root_custody();
|
||||||
|
|
||||||
|
if (!root)
|
||||||
|
root = VFS::the().root_custody();
|
||||||
|
|
||||||
auto* process = new Process(first_thread, parts.take_last(), uid, gid, parent_pid, Ring3, move(cwd), nullptr, tty);
|
auto* process = new Process(first_thread, parts.take_last(), uid, gid, parent_pid, Ring3, move(cwd), nullptr, tty);
|
||||||
|
|
||||||
error = process->exec(path, move(arguments), move(environment));
|
error = process->exec(path, move(arguments), move(environment));
|
||||||
|
@ -2650,6 +2657,7 @@ void Process::finalize()
|
||||||
m_tty = nullptr;
|
m_tty = nullptr;
|
||||||
m_executable = nullptr;
|
m_executable = nullptr;
|
||||||
m_cwd = nullptr;
|
m_cwd = nullptr;
|
||||||
|
m_root_directory = nullptr;
|
||||||
m_elf_loader = nullptr;
|
m_elf_loader = nullptr;
|
||||||
|
|
||||||
disown_all_shared_buffers();
|
disown_all_shared_buffers();
|
||||||
|
@ -4127,3 +4135,29 @@ int Process::sys$set_process_boost(pid_t pid, int amount)
|
||||||
process->m_priority_boost = amount;
|
process->m_priority_boost = amount;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Process::sys$chroot(const char* user_path, size_t path_length)
|
||||||
|
{
|
||||||
|
if (!is_superuser())
|
||||||
|
return -EPERM;
|
||||||
|
auto path = get_syscall_path_argument(user_path, path_length);
|
||||||
|
if (path.is_error())
|
||||||
|
return path.error();
|
||||||
|
auto directory_or_error = VFS::the().open_directory(path.value(), current_directory());
|
||||||
|
if (directory_or_error.is_error())
|
||||||
|
return directory_or_error.error();
|
||||||
|
set_root_directory(Custody::create(nullptr, "", directory_or_error.value()->inode()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Custody& Process::root_directory()
|
||||||
|
{
|
||||||
|
if (!m_root_directory)
|
||||||
|
m_root_directory = VFS::the().root_custody();
|
||||||
|
return *m_root_directory;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Process::set_root_directory(const Custody& root)
|
||||||
|
{
|
||||||
|
m_root_directory = root;
|
||||||
|
}
|
||||||
|
|
|
@ -229,6 +229,7 @@ public:
|
||||||
int sys$futex(const Syscall::SC_futex_params*);
|
int sys$futex(const Syscall::SC_futex_params*);
|
||||||
int sys$set_thread_boost(int tid, int amount);
|
int sys$set_thread_boost(int tid, int amount);
|
||||||
int sys$set_process_boost(pid_t, int amount);
|
int sys$set_process_boost(pid_t, int amount);
|
||||||
|
int sys$chroot(const char* path, size_t path_length);
|
||||||
|
|
||||||
static void initialize();
|
static void initialize();
|
||||||
|
|
||||||
|
@ -309,6 +310,9 @@ public:
|
||||||
|
|
||||||
u32 priority_boost() const { return m_priority_boost; }
|
u32 priority_boost() const { return m_priority_boost; }
|
||||||
|
|
||||||
|
Custody& root_directory();
|
||||||
|
void set_root_directory(const Custody&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class MemoryManager;
|
friend class MemoryManager;
|
||||||
friend class Scheduler;
|
friend class Scheduler;
|
||||||
|
@ -369,6 +373,7 @@ private:
|
||||||
|
|
||||||
RefPtr<Custody> m_executable;
|
RefPtr<Custody> m_executable;
|
||||||
RefPtr<Custody> m_cwd;
|
RefPtr<Custody> m_cwd;
|
||||||
|
RefPtr<Custody> m_root_directory;
|
||||||
|
|
||||||
RefPtr<TTY> m_tty;
|
RefPtr<TTY> m_tty;
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,8 @@ typedef u32 socklen_t;
|
||||||
__ENUMERATE_SYSCALL(get_kernel_info_page) \
|
__ENUMERATE_SYSCALL(get_kernel_info_page) \
|
||||||
__ENUMERATE_SYSCALL(futex) \
|
__ENUMERATE_SYSCALL(futex) \
|
||||||
__ENUMERATE_SYSCALL(set_thread_boost) \
|
__ENUMERATE_SYSCALL(set_thread_boost) \
|
||||||
__ENUMERATE_SYSCALL(set_process_boost)
|
__ENUMERATE_SYSCALL(set_process_boost) \
|
||||||
|
__ENUMERATE_SYSCALL(chroot)
|
||||||
|
|
||||||
namespace Syscall {
|
namespace Syscall {
|
||||||
|
|
||||||
|
|
|
@ -145,6 +145,8 @@ VFS* vfs;
|
||||||
hang();
|
hang();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current->process().set_root_directory(vfs->root_custody());
|
||||||
|
|
||||||
dbgprintf("Load ksyms\n");
|
dbgprintf("Load ksyms\n");
|
||||||
load_ksyms();
|
load_ksyms();
|
||||||
dbgprintf("Loaded ksyms\n");
|
dbgprintf("Loaded ksyms\n");
|
||||||
|
|
|
@ -617,4 +617,14 @@ int get_process_name(char* buffer, int buffer_size)
|
||||||
int rc = syscall(SC_get_process_name, buffer, buffer_size);
|
int rc = syscall(SC_get_process_name, buffer, buffer_size);
|
||||||
__RETURN_WITH_ERRNO(rc, rc, -1);
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int chroot(const char* path)
|
||||||
|
{
|
||||||
|
if (!path) {
|
||||||
|
errno = EFAULT;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int rc = syscall(SC_chroot, path, strlen(path));
|
||||||
|
__RETURN_WITH_ERRNO(rc, rc, -1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ int execvpe(const char* filename, char* const argv[], char* const envp[]);
|
||||||
int execvp(const char* filename, char* const argv[]);
|
int execvp(const char* filename, char* const argv[]);
|
||||||
int execl(const char* filename, const char* arg, ...);
|
int execl(const char* filename, const char* arg, ...);
|
||||||
int execlp(const char* filename, const char* arg, ...);
|
int execlp(const char* filename, const char* arg, ...);
|
||||||
|
int chroot(const char* path);
|
||||||
void sync();
|
void sync();
|
||||||
void _exit(int status);
|
void _exit(int status);
|
||||||
pid_t getsid(pid_t);
|
pid_t getsid(pid_t);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue