Selaa lähdekoodia

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.
Andreas Kling 5 vuotta sitten
vanhempi
commit
ddd0b19281

+ 8 - 6
Kernel/FileSystem/VirtualFileSystem.cpp

@@ -52,7 +52,7 @@ KResult VFS::mount(NonnullRefPtr<FS>&& file_system, Custody& mount_point)
 KResult VFS::mount(NonnullRefPtr<FS>&& file_system, StringView path)
 {
     LOCKER(m_lock);
-    auto result = resolve_path(path, root_custody());
+    auto result = resolve_path(path, current->process().root_directory());
     if (result.is_error()) {
         dbg() << "VFS: mount can't resolve mount point '" << path << "'";
         return result.error();
@@ -667,11 +667,13 @@ KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& ba
     auto parts = path.split_view('/', true);
     InodeIdentifier crumb_id;
 
+    auto& current_root = current->process().root_directory();
+
     NonnullRefPtrVector<Custody, 32> custody_chain;
 
     if (path[0] == '/') {
-        custody_chain.append(root_custody());
-        crumb_id = root_inode_id();
+        custody_chain.append(current_root);
+        crumb_id = current_root.inode().identifier();
     } else {
         for (auto* custody = &base; custody; custody = custody->parent()) {
             // 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();
 
     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);
         if (!crumb_inode)
             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))
             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 dir_inode = get_inode(mount->host());
             ASSERT(dir_inode);
@@ -724,7 +726,7 @@ KResultOr<NonnullRefPtr<Custody>> VFS::resolve_path(StringView path, Custody& ba
         crumb_inode = get_inode(crumb_id);
         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();
         if (metadata.is_directory()) {

+ 35 - 1
Kernel/Process.cpp

@@ -565,6 +565,7 @@ pid_t Process::sys$fork(RegisterDump& regs)
 {
     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);
+    child->m_root_directory = m_root_directory;
 
 #ifdef FORK_DEBUG
     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());
     }
     RefPtr<Custody> cwd;
+    RefPtr<Custody> root;
     {
         InterruptDisabler disabler;
-        if (auto* parent = Process::from_pid(parent_pid))
+        if (auto* parent = Process::from_pid(parent_pid)) {
             cwd = parent->m_cwd;
+            root = parent->m_root_directory;
+        }
     }
 
     if (!cwd)
         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);
 
     error = process->exec(path, move(arguments), move(environment));
@@ -2650,6 +2657,7 @@ void Process::finalize()
     m_tty = nullptr;
     m_executable = nullptr;
     m_cwd = nullptr;
+    m_root_directory = nullptr;
     m_elf_loader = nullptr;
 
     disown_all_shared_buffers();
@@ -4127,3 +4135,29 @@ int Process::sys$set_process_boost(pid_t pid, int amount)
     process->m_priority_boost = amount;
     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;
+}

+ 5 - 0
Kernel/Process.h

@@ -229,6 +229,7 @@ public:
     int sys$futex(const Syscall::SC_futex_params*);
     int sys$set_thread_boost(int tid, int amount);
     int sys$set_process_boost(pid_t, int amount);
+    int sys$chroot(const char* path, size_t path_length);
 
     static void initialize();
 
@@ -309,6 +310,9 @@ public:
 
     u32 priority_boost() const { return m_priority_boost; }
 
+    Custody& root_directory();
+    void set_root_directory(const Custody&);
+
 private:
     friend class MemoryManager;
     friend class Scheduler;
@@ -369,6 +373,7 @@ private:
 
     RefPtr<Custody> m_executable;
     RefPtr<Custody> m_cwd;
+    RefPtr<Custody> m_root_directory;
 
     RefPtr<TTY> m_tty;
 

+ 2 - 1
Kernel/Syscall.h

@@ -147,7 +147,8 @@ typedef u32 socklen_t;
     __ENUMERATE_SYSCALL(get_kernel_info_page)       \
     __ENUMERATE_SYSCALL(futex)                      \
     __ENUMERATE_SYSCALL(set_thread_boost)           \
-    __ENUMERATE_SYSCALL(set_process_boost)
+    __ENUMERATE_SYSCALL(set_process_boost)          \
+    __ENUMERATE_SYSCALL(chroot)
 
 namespace Syscall {
 

+ 2 - 0
Kernel/init.cpp

@@ -145,6 +145,8 @@ VFS* vfs;
         hang();
     }
 
+    current->process().set_root_directory(vfs->root_custody());
+
     dbgprintf("Load ksyms\n");
     load_ksyms();
     dbgprintf("Loaded ksyms\n");

+ 10 - 0
Libraries/LibC/unistd.cpp

@@ -617,4 +617,14 @@ int get_process_name(char* buffer, int buffer_size)
     int rc = syscall(SC_get_process_name, buffer, buffer_size);
     __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);
+}
 }

+ 1 - 0
Libraries/LibC/unistd.h

@@ -51,6 +51,7 @@ int execvpe(const char* filename, char* const argv[], char* const envp[]);
 int execvp(const char* filename, char* const argv[]);
 int execl(const char* filename, const char* arg, ...);
 int execlp(const char* filename, const char* arg, ...);
+int chroot(const char* path);
 void sync();
 void _exit(int status);
 pid_t getsid(pid_t);